diff --git a/agent/src/main/java/com/cloud/agent/Agent.java b/agent/src/main/java/com/cloud/agent/Agent.java index d2d4f165979d..15f010808aca 100644 --- a/agent/src/main/java/com/cloud/agent/Agent.java +++ b/agent/src/main/java/com/cloud/agent/Agent.java @@ -504,6 +504,13 @@ protected void setupStartupCommand(final StartupCommand startup) { startup.setGuid(getResourceGuid()); startup.setResourceName(getResourceName()); startup.setVersion(getVersion()); + startup.setArch(getAgentArch()); + } + + protected String getAgentArch() { + final Script command = new Script("/usr/bin/arch", 500, logger); + final OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser(); + return command.execute(parser); } @Override @@ -858,11 +865,21 @@ public void processReadyCommand(final Command cmd) { setId(ready.getHostId()); } + verifyAgentArch(ready.getArch()); processManagementServerList(ready.getMsHostList(), ready.getLbAlgorithm(), ready.getLbCheckInterval()); logger.info("Ready command is processed for agent id = {}", getId()); } + private void verifyAgentArch(String arch) { + if (StringUtils.isNotBlank(arch)) { + String agentArch = getAgentArch(); + if (!arch.equals(agentArch)) { + logger.error("Unexpected arch {}, expected {}", agentArch, arch); + } + } + } + public void processOtherTask(final Task task) { final Object obj = task.get(); if (obj instanceof Response) { 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 d08884d1cbe0..25c75001a3c1 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 FirewallRuleTO(FirewallRule rule, String srcVlanTag, String srcIp, Firewa 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 000000000000..b0e5394df75e --- /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 000000000000..ae877ee60db7 --- /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 000000000000..935237092dd7 --- /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/cpu/CPU.java b/api/src/main/java/com/cloud/cpu/CPU.java new file mode 100644 index 000000000000..4e1b9f5a5011 --- /dev/null +++ b/api/src/main/java/com/cloud/cpu/CPU.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 com.cloud.cpu; + +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.commons.lang3.StringUtils; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class CPU { + + public static final String archX86Identifier = "i686"; + public static final String archX86_64Identifier = "x86_64"; + public static final String archARM64Identifier = "aarch64"; + + public static class CPUArch { + private static final Map cpuArchMap = new LinkedHashMap<>(); + + public static final CPUArch archX86 = new CPUArch(archX86Identifier, 32); + public static final CPUArch amd64 = new CPUArch(archX86_64Identifier, 64); + public static final CPUArch arm64 = new CPUArch(archARM64Identifier, 64); + + private String type; + private int bits; + + public CPUArch(String type, int bits) { + this.type = type; + this.bits = bits; + cpuArchMap.put(type, this); + } + + public String getType() { + return this.type; + } + + public int getBits() { + return this.bits; + } + + public static CPUArch fromType(String type) { + if (StringUtils.isBlank(type)) { + return amd64; + } + switch (type) { + case archX86Identifier: return archX86; + case archX86_64Identifier: return amd64; + case archARM64Identifier: return arm64; + default: throw new CloudRuntimeException(String.format("Unsupported arch type: %s", type)); + } + } + } +} diff --git a/api/src/main/java/com/cloud/dc/DedicatedResources.java b/api/src/main/java/com/cloud/dc/DedicatedResources.java index 63188ca0b0e9..23e6cc88a1e0 100644 --- a/api/src/main/java/com/cloud/dc/DedicatedResources.java +++ b/api/src/main/java/com/cloud/dc/DedicatedResources.java @@ -21,6 +21,10 @@ import org.apache.cloudstack.api.InternalIdentity; public interface DedicatedResources extends InfrastructureEntity, InternalIdentity, Identity { + enum Type { + Zone, Pod, Cluster, Host + } + @Override long getId(); diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java index 050acdc84bca..5e5309965c1e 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.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/host/Host.java b/api/src/main/java/com/cloud/host/Host.java index 4a3b914364f8..56b4ed75a311 100644 --- a/api/src/main/java/com/cloud/host/Host.java +++ b/api/src/main/java/com/cloud/host/Host.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.host; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.resource.ResourceState; import com.cloud.utils.fsm.StateObject; @@ -208,4 +209,6 @@ public static String[] toStrings(Host.Type... types) { boolean isDisabled(); ResourceState getResourceState(); + + CPU.CPUArch getArch(); } diff --git a/api/src/main/java/com/cloud/network/Network.java b/api/src/main/java/com/cloud/network/Network.java index 3b13ef7bd9cc..d3bc5005cb7a 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 @@ class Service { 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 void setIp6Address(String ip6Address) { 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 void setIp6Address(String ip6Address) { 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 0d81495489cc..d7de9df5325d 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 1a5c80ea8718..83dc247cc9ee 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 NetworkProfile(Network network) { isRedundant = network.isRedundant(); isRollingRestart = network.isRollingRestart(); externalId = network.getExternalId(); + networkCidrSize = network.getNetworkCidrSize(); } @Override @@ -210,11 +212,21 @@ public String getGateway() { 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 Integer getPrivateMtu() { 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 000000000000..ee919cb1af7d --- /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/nsx/NsxService.java b/api/src/main/java/com/cloud/network/nsx/NsxService.java index 79ad9547c736..bc4e6aafbfec 100644 --- a/api/src/main/java/com/cloud/network/nsx/NsxService.java +++ b/api/src/main/java/com/cloud/network/nsx/NsxService.java @@ -18,9 +18,19 @@ import com.cloud.network.IpAddress; import com.cloud.network.vpc.Vpc; +import org.apache.cloudstack.framework.config.ConfigKey; public interface NsxService { + ConfigKey NSX_API_FAILURE_RETRIES = new ConfigKey<>("Advanced", Integer.class, + "nsx.api.failure.retries", "30", + "Number of retries for NSX API operations in case of failures", + true, ConfigKey.Scope.Zone); + ConfigKey NSX_API_FAILURE_INTERVAL = new ConfigKey<>("Advanced", Integer.class, + "nsx.api.failure.interval", "60", + "Waiting time (in seconds) before retrying an NSX API operation in case of failure", + true, ConfigKey.Scope.Zone); + boolean createVpcNetwork(Long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled); boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address); } 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 3aab57d5d3d2..38263f59667c 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 @@ 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 enum State { boolean isForNsx(); - String getNsxMode(); + NetworkOffering.NetworkMode getNetworkMode(); /** * @return service offering id used by VPC virtual router @@ -79,4 +80,8 @@ public enum State { 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 1ce3cf8ab0e9..10f1ddcc12d6 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.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 0f0d29f4082c..af2a9847a62d 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 cf01fbf30e20..7011aea679ee 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 enum Detail { 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 enum NsxMode { boolean isForNsx(); - String getNsxMode(); + NetworkMode getNetworkMode(); TrafficType getTrafficType(); @@ -165,4 +169,8 @@ public enum NsxMode { String getServicePackage(); Date getCreated(); + + RoutingMode getRoutingMode(); + + Boolean isSpecifyAsNumber(); } diff --git a/api/src/main/java/com/cloud/org/Cluster.java b/api/src/main/java/com/cloud/org/Cluster.java index 4079c88dfde3..5124168084c6 100644 --- a/api/src/main/java/com/cloud/org/Cluster.java +++ b/api/src/main/java/com/cloud/org/Cluster.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.org; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.org.Managed.ManagedState; import org.apache.cloudstack.kernel.Partition; @@ -38,4 +39,6 @@ public static enum ClusterType { AllocationState getAllocationState(); ManagedState getManagedState(); + + CPU.CPUArch getArch(); } diff --git a/api/src/main/java/com/cloud/storage/Volume.java b/api/src/main/java/com/cloud/storage/Volume.java index 40c5660b2df2..c7fbdb0a5445 100644 --- a/api/src/main/java/com/cloud/storage/Volume.java +++ b/api/src/main/java/com/cloud/storage/Volume.java @@ -271,11 +271,13 @@ enum Event { void setExternalUuid(String externalUuid); - public Long getPassphraseId(); + Long getPassphraseId(); - public void setPassphraseId(Long id); + void setPassphraseId(Long id); - public String getEncryptFormat(); + String getEncryptFormat(); - public void setEncryptFormat(String encryptFormat); + void setEncryptFormat(String encryptFormat); + + boolean isDeleteProtection(); } diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java b/api/src/main/java/com/cloud/storage/VolumeApiService.java index f9cba14679e0..6f4c7aa09e26 100644 --- a/api/src/main/java/com/cloud/storage/VolumeApiService.java +++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java @@ -117,7 +117,9 @@ Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account acc Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List zoneIds) throws ResourceAllocationException; - Volume updateVolume(long volumeId, String path, String state, Long storageId, Boolean displayVolume, String customId, long owner, String chainInfo, String name); + Volume updateVolume(long volumeId, String path, String state, Long storageId, + Boolean displayVolume, Boolean deleteProtection, + String customId, long owner, String chainInfo, String name); /** * Extracts the volume to a particular location. diff --git a/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java b/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java index 1f8cef0365b1..d8872d5fe724 100644 --- a/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java +++ b/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java @@ -19,6 +19,7 @@ import java.util.Date; import java.util.Map; +import com.cloud.cpu.CPU; import com.cloud.user.UserData; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.api.Identity; @@ -148,4 +149,6 @@ public enum TemplateFilter { UserData.UserDataOverridePolicy getUserDataOverridePolicy(); + CPU.CPUArch getArch(); + } diff --git a/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java b/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java index 6cda8d07bd86..69f391a5656a 100644 --- a/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java +++ b/api/src/main/java/org/apache/cloudstack/affinity/AffinityGroupResponse.java @@ -25,6 +25,7 @@ import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; import org.apache.cloudstack.api.response.ControlledViewEntityResponse; +import org.apache.cloudstack.dedicated.DedicatedResourceResponse; import com.cloud.serializer.Param; @@ -76,6 +77,10 @@ public class AffinityGroupResponse extends BaseResponse implements ControlledVie @Param(description = "virtual machine IDs associated with this affinity group") private List vmIdList; + @SerializedName("dedicatedresources") + @Param(description = "dedicated resources associated with this affinity group") + private List dedicatedResources; + public AffinityGroupResponse() { } @@ -171,4 +176,12 @@ public void addVMId(String vmId) { this.vmIdList.add(vmId); } + public void addDedicatedResource(DedicatedResourceResponse dedicatedResourceResponse) { + if (this.dedicatedResources == null) { + this.dedicatedResources = new ArrayList<>(); + } + + this.dedicatedResources.add(dedicatedResourceResponse); + } + } 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 21da4025e846..bb16b0ff90de 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,18 @@ 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 ARCH = "arch"; + 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 +54,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 +97,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"; @@ -127,6 +138,7 @@ public class ApiConstants { public static final String DATACENTER_NAME = "datacentername"; public static final String DATADISK_OFFERING_LIST = "datadiskofferinglist"; public static final String DEFAULT_VALUE = "defaultvalue"; + public static final String DELETE_PROTECTION = "deleteprotection"; public static final String DESCRIPTION = "description"; public static final String DESTINATION = "destination"; public static final String DESTINATION_ZONE_ID = "destzoneid"; @@ -171,6 +183,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 +328,9 @@ 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 MULTI_ARCH = "ismultiarch"; 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 +371,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 +457,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 +536,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 +565,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 +707,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 +724,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 +987,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 b206cd011c1d..457afdc88478 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 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.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 static enum CommandType { 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/BaseUpdateTemplateOrIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java index e3aead6881ba..9a8282df1121 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java @@ -16,8 +16,10 @@ // under the License. package org.apache.cloudstack.api; +import com.cloud.cpu.CPU; import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.TemplateResponse; +import org.apache.commons.lang3.StringUtils; import java.util.Collection; import java.util.Map; @@ -77,6 +79,11 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends BaseCmd { description = "optional boolean field, which indicates if details should be cleaned up or not (if set to true, details removed for this resource, details field ignored; if false or not set, no action)") private Boolean cleanupDetails; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the template/ISO. Valid options are: x86_64, aarch64", + since = "4.20") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -141,4 +148,11 @@ public Map getDetails() { public boolean isCleanupDetails(){ return cleanupDetails == null ? false : cleanupDetails.booleanValue(); } + + public CPU.CPUArch getCPUArch() { + if (StringUtils.isBlank(arch)) { + return null; + } + return CPU.CPUArch.fromType(arch); + } } 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 46a810c6f3b4..ea0d946ee417 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.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.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.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 @@ List createTemplateResponses(ResponseView view, VirtualMachine 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 000000000000..beacba850c3f --- /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 000000000000..33e139315bf6 --- /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 000000000000..82e545811029 --- /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 000000000000..687f60dc6da8 --- /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/cluster/AddClusterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java index 184a443d9db9..69cb43ce40ec 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.List; +import com.cloud.cpu.CPU; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.APICommand; @@ -67,6 +68,11 @@ public class AddClusterCmd extends BaseCmd { description = "hypervisor type of the cluster: XenServer,KVM,VMware,Hyperv,BareMetal,Simulator,Ovm3") private String hypervisor; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the cluster. Valid options are: x86_64, aarch64", + since = "4.20") + private String arch; + @Parameter(name = ApiConstants.CLUSTER_TYPE, type = CommandType.STRING, required = true, description = "type of the cluster: CloudManaged, ExternalManaged") private String clusterType; @@ -204,6 +210,10 @@ public ApiCommandResourceType getApiResourceType() { return ApiCommandResourceType.Cluster; } + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + @Override public void execute() { try { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java index 77bb97fd39d7..c4ee87380ed9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.admin.cluster; +import com.cloud.cpu.CPU; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.APICommand; @@ -29,6 +30,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.org.Cluster; import com.cloud.user.Account; +import org.apache.commons.lang3.StringUtils; @APICommand(name = "updateCluster", description = "Updates an existing cluster", responseObject = ClusterResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -53,6 +55,11 @@ public class UpdateClusterCmd extends BaseCmd { @Parameter(name = ApiConstants.MANAGED_STATE, type = CommandType.STRING, description = "whether this cluster is managed by cloudstack") private String managedState; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the cluster. Valid options are: x86_64, aarch64", + since = "4.20") + private String arch; + public String getClusterName() { return clusterName; } @@ -108,6 +115,13 @@ public ApiCommandResourceType getApiResourceType() { return ApiCommandResourceType.Cluster; } + public CPU.CPUArch getArch() { + if (StringUtils.isBlank(arch)) { + return null; + } + return CPU.CPUArch.fromType(arch); + } + @Override public void execute() { Cluster cluster = _resourceService.getCluster(getId()); 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 000000000000..a482cb1d4f27 --- /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 000000000000..5f48cf9c6327 --- /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 cd9770877ed7..d8b57f79528c 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.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 String getRouterIp() { 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 9117bcfc193a..af3db374a7c9 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 boolean isForNsx() { return BooleanUtils.isTrue(forNsx); } - public String getNsxMode() { - return nsxMode; + public String getNetworkMode() { + return networkMode; } public boolean getNsxSupportsLbService() { @@ -462,6 +472,14 @@ public Boolean getEnable() { 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 000000000000..2df032c559c5 --- /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 000000000000..28a646f9d036 --- /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 000000000000..222bc1bad98d --- /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 000000000000..9761f6e89ebc --- /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 000000000000..2c2182250edb --- /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 000000000000..3e151b9b58f4 --- /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 000000000000..da7a23f50d9c --- /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 000000000000..1d6bffca342e --- /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 000000000000..0c89f3f1d43c --- /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 000000000000..80642124938a --- /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 000000000000..ec3d0ea11629 --- /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 000000000000..a01711efa44f --- /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 000000000000..ea15f0970e87 --- /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 000000000000..92610c233ef0 --- /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 000000000000..ae44330ea033 --- /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 bd00876ed36c..9dc31f8cefec 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 dd5c815238e1..73b4f5df196b 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 boolean isForNsx() { return BooleanUtils.isTrue(forNsx); } - public String getNsxMode() { - return nsxMode; + public String getNetworkMode() { + return networkMode; } public boolean getNsxSupportsLbService() { @@ -265,6 +275,14 @@ public Boolean getEnable() { 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 000000000000..b835f7225df3 --- /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 4e3cf4621efd..18af5b2973ee 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.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/iso/ListIsosCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java index 04dcbf8ca964..5c4d606a93cd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.iso; +import com.cloud.cpu.CPU; import com.cloud.server.ResourceIcon; import com.cloud.server.ResourceTag; import org.apache.cloudstack.api.response.ResourceIconResponse; @@ -34,6 +35,7 @@ import com.cloud.template.VirtualMachineTemplate.TemplateFilter; import com.cloud.user.Account; +import org.apache.commons.lang3.StringUtils; import java.util.List; @@ -88,6 +90,11 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, description = "flag to display the resource image for the isos") private Boolean showIcon; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the ISO. Valid options are: x86_64, aarch64", + since = "4.20") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -159,6 +166,13 @@ public boolean listInReadyState() { return onlyReady; } + public CPU.CPUArch getArch() { + if (StringUtils.isBlank(arch)) { + return null; + } + return CPU.CPUArch.fromType(arch); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java index becfdcd653d3..81f525522895 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java @@ -18,6 +18,7 @@ import java.util.List; +import com.cloud.cpu.CPU; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -118,6 +119,11 @@ public class RegisterIsoCmd extends BaseCmd implements UserCmd { description = "true if password reset feature is supported; default is false") private Boolean passwordEnabled; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the ISO. Valid options are: x86_64, aarch64", + since = "4.20") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -229,6 +235,14 @@ public boolean isPasswordEnabled() { return passwordEnabled == null ? false : passwordEnabled; } + public void setArch(String arch) { + this.arch = arch; + } + + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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 2395339a477c..aca3d3ca1b45 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 String getIp6Cidr() { return NetUtils.standardizeIp6Cidr(ip6Cidr); } + public Integer getCidrSize() { + return cidrSize; + } + public Long getAclId() { return aclId; } @@ -391,6 +403,10 @@ public String getIp6Dns2() { 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 33f452008d99..bdc89d804cdf 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 Boolean getForVpc() { 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 000000000000..7146d1ae1d19 --- /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 000000000000..16696f5f71b7 --- /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 000000000000..3fdf3b0f5b46 --- /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 000000000000..c6f6034b1ba1 --- /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/template/GetUploadParamsForTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/GetUploadParamsForTemplateCmd.java index c878fda82409..8fa1a5d53eb7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/GetUploadParamsForTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/GetUploadParamsForTemplateCmd.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Map; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; @@ -55,6 +56,11 @@ public class GetUploadParamsForTemplateCmd extends AbstractGetUploadParamsCmd { description = "the ID of the OS Type that best represents the OS of this template. Not required for VMware as the guest OS is obtained from the OVF file.") private Long osTypeId; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the template. Valid options are: x86_64, aarch64", + since = "4.20") + private String arch; + @Parameter(name = ApiConstants.BITS, type = CommandType.INTEGER, description = "32 or 64 bits support. 64 by default") private Integer bits; @@ -162,6 +168,10 @@ public boolean isDeployAsIs() { Boolean.TRUE.equals(deployAsIs); } + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + @Override public void execute() throws ServerApiException { validateRequest(); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java index 113080257d02..bff65ef70a92 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.template; +import com.cloud.cpu.CPU; import com.cloud.exception.InvalidParameterValueException; import com.cloud.server.ResourceIcon; import com.cloud.server.ResourceTag; @@ -41,6 +42,7 @@ import com.cloud.template.VirtualMachineTemplate; import com.cloud.template.VirtualMachineTemplate.TemplateFilter; import com.cloud.user.Account; +import org.apache.commons.lang3.StringUtils; @APICommand(name = "listTemplates", description = "List all public, private, and privileged templates.", responseObject = TemplateResponse.class, entityType = {VirtualMachineTemplate.class}, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -104,6 +106,11 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User since = "4.19.0") private Boolean isVnf; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the template. Valid options are: x86_64, aarch64", + since = "4.20") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -191,6 +198,13 @@ public Boolean getVnf() { return isVnf; } + public CPU.CPUArch getArch() { + if (StringUtils.isBlank(arch)) { + return null; + } + return CPU.CPUArch.fromType(arch); + } + @Override public String getCommandName() { return s_name; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java index 1e5c4af9c154..a7826dedcd04 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.template; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor; import java.net.URISyntaxException; import java.util.ArrayList; @@ -172,6 +173,11 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { since = "4.19.0") private String templateType; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the template. Valid options are: x86_64, aarch64", + since = "4.20") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -293,6 +299,10 @@ public String getTemplateType() { return templateType; } + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java index e5d625fe70d5..0f5dade96d25 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java @@ -146,6 +146,14 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, @Parameter(name = ApiConstants.EXTRA_CONFIG, type = CommandType.STRING, since = "4.12", description = "an optional URL encoded string that can be passed to the virtual machine upon successful deployment", length = 5120) private String extraConfig; + @Parameter(name = ApiConstants.DELETE_PROTECTION, + type = CommandType.BOOLEAN, since = "4.20.0", + description = "Set delete protection for the virtual machine. If " + + "true, the instance will be protected from deletion. " + + "Note: If the instance is managed by another service like" + + " autoscaling groups or CKS, delete protection will be ignored.") + private Boolean deleteProtection; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -215,6 +223,10 @@ public boolean isCleanupDetails(){ return cleanupDetails == null ? false : cleanupDetails.booleanValue(); } + public Boolean getDeleteProtection() { + return deleteProtection; + } + public Map> getDhcpOptionsMap() { Map> dhcpOptionsMap = new HashMap<>(); if (dhcpOptionsNetworkList != null && !dhcpOptionsNetworkList.isEmpty()) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/UpdateVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/UpdateVolumeCmd.java index 467c587cc731..22b819c8cba2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/UpdateVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/UpdateVolumeCmd.java @@ -77,6 +77,14 @@ public class UpdateVolumeCmd extends BaseAsyncCustomIdCmd implements UserCmd { @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "new name of the volume", since = "4.16") private String name; + @Parameter(name = ApiConstants.DELETE_PROTECTION, + type = CommandType.BOOLEAN, since = "4.20.0", + description = "Set delete protection for the volume. If true, The volume " + + "will be protected from deletion. Note: If the volume is managed by " + + "another service like autoscaling groups or CKS, delete protection will be " + + "ignored.") + private Boolean deleteProtection; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -109,6 +117,10 @@ public String getName() { return name; } + public Boolean getDeleteProtection() { + return deleteProtection; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -168,7 +180,7 @@ public String getEventDescription() { public void execute() { CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId())); Volume result = _volumeService.updateVolume(getId(), getPath(), getState(), getStorageId(), getDisplayVolume(), - getCustomId(), getEntityOwnerId(), getChainInfo(), getName()); + getDeleteProtection(), getCustomId(), getEntityOwnerId(), getChainInfo(), getName()); if (result != null) { VolumeResponse response = _responseGenerator.createVolumeResponse(getResponseView(), result); response.setResponseName(getCommandName()); 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 89a65f8c27cc..2f62d0d7210d 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 String getCidr() { return cidr; } + public Integer getCidrSize() { + return cidrSize; + } + public String getDisplayText() { return StringUtils.isEmpty(displayText) ? vpcName : displayText; } @@ -189,6 +201,10 @@ public String getSourceNatIP() { 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 000000000000..86dab54ca6b8 --- /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 000000000000..45884250984e --- /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 000000000000..344e65c6badc --- /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/ClusterResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java index ca01a2012f6a..1c69849239f9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java @@ -91,6 +91,10 @@ public class ClusterResponse extends BaseResponseWithAnnotations { @Param(description = "Meta data associated with the zone (key/value pairs)") private Map resourceDetails; + @SerializedName(ApiConstants.ARCH) + @Param(description = "CPU Arch of the hosts in the cluster", since = "4.20") + private String arch; + public String getId() { return id; } @@ -247,4 +251,12 @@ public void setOvm3vip(String ovm3vip) { public void setCapacities(List capacities) { this.capacities = capacities; } + + public void setArch(String arch) { + this.arch = arch; + } + + public String getArch() { + return arch; + } } 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 000000000000..a1a87794a888 --- /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/HostResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java index 274c1fd43c1d..62bcc07b16d9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java @@ -290,6 +290,10 @@ public class HostResponse extends BaseResponseWithAnnotations { @Param(description = "true if the host supports instance conversion (using virt-v2v)", since = "4.19.1") private Boolean instanceConversionSupported; + @SerializedName(ApiConstants.ARCH) + @Param(description = "CPU Arch of the host", since = "4.20") + private String arch; + @Override public String getObjectId() { return this.getId(); @@ -787,6 +791,14 @@ public void setIsTagARule(Boolean tagARule) { isTagARule = tagARule; } + public void setArch(String arch) { + this.arch = arch; + } + + public String getArch() { + return arch; + } + public Long getCpuAllocatedValue() { return cpuAllocatedValue; } 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 000000000000..136c87971b79 --- /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 000000000000..1430bcd059c1 --- /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 b73163a5d057..81a8129ecb76 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 void setForTungsten(Boolean forTungsten) { 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 String getInternetProtocol() { 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 1049740bf55a..a80317c83cd8 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 void setVlan(String vlan) { 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 void setInternetProtocol(String internetProtocol) { 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 void addIpv6Route(Ipv6RouteResponse ipv6Route) { 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/TemplateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java index 604c9f0955f2..98e96091d8c7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java @@ -183,6 +183,10 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements @Param(description = "Lists the download progress of a template across all secondary storages") private List> downloadDetails; + @SerializedName(ApiConstants.ARCH) + @Param(description = "CPU Arch of the template", since = "4.20") + private String arch; + @SerializedName(ApiConstants.BITS) @Param(description = "the processor bit size", since = "4.10") private int bits; @@ -520,4 +524,8 @@ public String getUserDataParams() { public void setUserDataParams(String userDataParams) { this.userDataParams = userDataParams; } + + public void setArch(String arch) { + this.arch = arch; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java index df9a474213b4..1f4b493fba2f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java @@ -320,6 +320,10 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @Param(description = "true if vm contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory.") private Boolean isDynamicallyScalable; + @SerializedName(ApiConstants.DELETE_PROTECTION) + @Param(description = "true if vm has delete protection.", since = "4.20.0") + private boolean deleteProtection; + @SerializedName(ApiConstants.SERVICE_STATE) @Param(description = "State of the Service from LB rule") private String serviceState; @@ -995,6 +999,14 @@ public void setDynamicallyScalable(Boolean dynamicallyScalable) { isDynamicallyScalable = dynamicallyScalable; } + public boolean isDeleteProtection() { + return deleteProtection; + } + + public void setDeleteProtection(boolean deleteProtection) { + this.deleteProtection = deleteProtection; + } + public String getOsTypeId() { return osTypeId; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java index 4ac17e9832b9..209ca57c50d2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VolumeResponse.java @@ -261,6 +261,10 @@ public class VolumeResponse extends BaseResponseWithTagInformation implements Co @Param(description = "true if storage snapshot is supported for the volume, false otherwise", since = "4.16") private boolean supportsStorageSnapshot; + @SerializedName(ApiConstants.DELETE_PROTECTION) + @Param(description = "true if volume has delete protection.", since = "4.20.0") + private boolean deleteProtection; + @SerializedName(ApiConstants.PHYSICAL_SIZE) @Param(description = "the bytes actually consumed on disk") private Long physicalsize; @@ -584,6 +588,14 @@ public boolean getSupportsStorageSnapshot() { return this.supportsStorageSnapshot; } + public boolean isDeleteProtection() { + return deleteProtection; + } + + public void setDeleteProtection(boolean deleteProtection) { + this.deleteProtection = deleteProtection; + } + public String getIsoId() { return isoId; } 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 ce00827f06d0..b11764da7d98 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 void setForNsx(Boolean forNsx) { 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 String getInternetProtocol() { 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 6f91da7d2d73..56479506686a 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 void setResourceIconResponse(ResourceIconResponse icon) { 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 void setIpv6Dns1(String ipv6Dns1) { 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 ff43fb697b5a..143dfad0eafd 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,14 @@ 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.MULTI_ARCH) + @Param(description = "true, if zone contains clusters and hosts from different CPU architectures", since = "4.20") + private boolean multiArch; + + @SerializedName(ApiConstants.ASN_RANGE) + @Param(description = "AS Number Range") + private String asnRange; + public ZoneResponse() { tags = new LinkedHashSet(); } @@ -392,4 +400,16 @@ public String getType() { public void setNsxEnabled(boolean nsxEnabled) { this.nsxEnabled = nsxEnabled; } + + public void setMultiArch(boolean multiArch) { + this.multiArch = multiArch; + } + + 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 000000000000..90d55cc57513 --- /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/dedicated/DedicatedResourceResponse.java b/api/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceResponse.java new file mode 100644 index 000000000000..fc116cbb91e3 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceResponse.java @@ -0,0 +1,44 @@ +// 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.dedicated; + +import com.cloud.dc.DedicatedResources; +import com.cloud.serializer.Param; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.BaseResponse; + +public class DedicatedResourceResponse extends BaseResponse { + @SerializedName("resourceid") + @Param(description = "the ID of the resource") + private String resourceId; + + @SerializedName("resourcename") + @Param(description = "the name of the resource") + private String resourceName; + + @SerializedName("resourcetype") + @Param(description = "the type of the resource") + private DedicatedResources.Type resourceType; + + public DedicatedResourceResponse(String resourceId, String resourceName, DedicatedResources.Type resourceType) { + this.resourceId = resourceId; + this.resourceName = resourceName; + this.resourceType = resourceType; + } +} 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 000000000000..e1d7eca0a03e --- /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 000000000000..b05033146169 --- /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 000000000000..569ed22c1640 --- /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 000000000000..2f704e9f47de --- /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 000000000000..603cda2040d0 --- /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 000000000000..2abcf736c5b5 --- /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 000000000000..7f49c61a6936 --- /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 000000000000..1b80e5bff7f0 --- /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 000000000000..e1393e316993 --- /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 000000000000..51c1eb986c47 --- /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 000000000000..7db77098b233 --- /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 000000000000..a4af5ddf748f --- /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 000000000000..7af173f09d96 --- /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 000000000000..cbfe34f774ac --- /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 000000000000..2c7a246f70f8 --- /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 000000000000..9ce9a4f9464f --- /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 000000000000..cdb9cce22d83 --- /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 000000000000..28ddad17afe5 --- /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 000000000000..96eb1f020de2 --- /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 000000000000..0d802bf36199 --- /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 000000000000..f3ae007da285 --- /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 000000000000..5e747188fda3 --- /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 000000000000..cb2027951adc --- /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 000000000000..8c55c4a73479 --- /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 000000000000..003944c61474 --- /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 000000000000..c4e21bb948b2 --- /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 000000000000..9d7f4ef7cf13 --- /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 000000000000..11c41f4c92d2 --- /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 000000000000..2b55d4c6a58d --- /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 000000000000..53ac45917cb8 --- /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 000000000000..dd0319df6960 --- /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 a28e9e9fd04f..2505c67e87db 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 void testGetCidr() { 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 000000000000..50248383b4f1 --- /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 000000000000..9515984134eb --- /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 000000000000..7c82eb843685 --- /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 000000000000..add9544de7d3 --- /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 000000000000..717668d054e6 --- /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 000000000000..6fb5141e7a98 --- /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 000000000000..2d1f8868ffc4 --- /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/ReadyCommand.java b/core/src/main/java/com/cloud/agent/api/ReadyCommand.java index 637e4f54da06..42f1d264a50d 100644 --- a/core/src/main/java/com/cloud/agent/api/ReadyCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ReadyCommand.java @@ -34,6 +34,7 @@ public ReadyCommand() { private String lbAlgorithm; private Long lbCheckInterval; private Boolean enableHumanReadableSizes; + private String arch; public ReadyCommand(Long dcId) { super(); @@ -94,4 +95,12 @@ public void setLbCheckInterval(Long lbCheckInterval) { public Boolean getEnableHumanReadableSizes() { return enableHumanReadableSizes; } + + public String getArch() { + return arch; + } + + public void setArch(String arch) { + this.arch = arch; + } } diff --git a/core/src/main/java/com/cloud/agent/api/StartupCommand.java b/core/src/main/java/com/cloud/agent/api/StartupCommand.java index 5f2c00d0be63..cca5e16b5854 100644 --- a/core/src/main/java/com/cloud/agent/api/StartupCommand.java +++ b/core/src/main/java/com/cloud/agent/api/StartupCommand.java @@ -47,6 +47,7 @@ public class StartupCommand extends Command { String resourceName; String gatewayIpAddress; String msHostList; + String arch; public StartupCommand(Host.Type type) { this.type = type; @@ -290,6 +291,14 @@ public void setMSHostList(String msHostList) { this.msHostList = msHostList; } + public String getArch() { + return arch; + } + + public void setArch(String arch) { + this.arch = arch; + } + @Override public boolean executeInSequence() { return false; diff --git a/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java b/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java index 2d4ed8c9cc42..286fced0c58a 100644 --- a/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java +++ b/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java @@ -32,6 +32,7 @@ public class StartupRoutingCommand extends StartupCommand { Integer cpuSockets; int cpus; long speed; + String cpuArch; long memory; long dom0MinMemory; boolean poolSync; @@ -201,4 +202,12 @@ public Boolean getHostHealthCheckResult() { public void setHostHealthCheckResult(Boolean hostHealthCheckResult) { this.hostHealthCheckResult = hostHealthCheckResult; } + + public String getCpuArch() { + return cpuArch; + } + + public void setCpuArch(String cpuArch) { + this.cpuArch = cpuArch; + } } 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 000000000000..9645b300db59 --- /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 000000000000..36371a196c81 --- /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 e435c838b7de..f9ea3e05e97f 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 46dd801bebf5..83dfa2a62caa 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.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 000000000000..68f4275bb6be --- /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 000000000000..54a1ab2e0915 --- /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 ade80d71384f..e370b764f226 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 000000000000..4cd15e4465a9 --- /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 000000000000..882c3b9da301 --- /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 000000000000..5f177c88abfc --- /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 000000000000..eba423e55ed8 --- /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/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java index f7d756268f44..287769d6a76d 100644 --- a/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java +++ b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java @@ -27,6 +27,7 @@ import java.text.SimpleDateFormat; import java.util.Date; +import com.cloud.cpu.CPU; import org.junit.Test; import com.cloud.agent.api.CheckOnHostCommand; @@ -272,7 +273,12 @@ public boolean isDisabled() { @Override public ResourceState getResourceState() { return ResourceState.Enabled; - }; + } + + @Override + public CPU.CPUArch getArch() { + return CPU.CPUArch.amd64; + } }; CheckOnHostCommand cohc = new CheckOnHostCommand(host); 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 84098bbc6541..d8e97f0277ba 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 @@ void prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationC 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 c877ebbe8d2b..01fd54430d6e 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 @@ NetworkOfferingVO createNetworkOffering(String name, String displayText, Traffic 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 24012a2487b8..a340f49c13f5 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,11 +110,13 @@ 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; /** - * Assigns source nat public IP address to VPC + * Assigns source nat public IP address to VPC. + * In case of NSX backed VPCs: CloudStack deploys VRs with Public NIC IP different to the VPC source NAT IP, the source NAT IP is on the NSX Public range * * @param owner * @param vpc @@ -122,7 +124,7 @@ public interface VpcManager { * @throws InsufficientAddressCapacityException * @throws ConcurrentOperationException */ - PublicIp assignSourceNatIpAddressToVpc(Account owner, Vpc vpc) throws InsufficientAddressCapacityException, ConcurrentOperationException; + PublicIp assignSourceNatIpAddressToVpc(Account owner, Vpc vpc, Long podId) throws InsufficientAddressCapacityException, ConcurrentOperationException; /** * Validates network offering to find if it can be used for network creation in VPC diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java index b693729f8413..27b3ac2d7511 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java @@ -39,6 +39,7 @@ import javax.naming.ConfigurationException; import com.cloud.configuration.Config; +import com.cloud.org.Cluster; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.GlobalLock; import org.apache.cloudstack.agent.lb.IndirectAgentLB; @@ -1097,6 +1098,7 @@ private AgentAttache sendReadyAndGetAttache(HostVO host, ReadyCommand ready, Lin agentMSHostList.addAll(Arrays.asList(msHosts[0].split(","))); } } + ready.setArch(host.getArch().getType()); AgentAttache attache = null; GlobalLock joinLock = getHostJoinLock(host.getId()); if (joinLock.lock(60)) { @@ -1128,6 +1130,7 @@ private AgentAttache handleConnectedAgent(final Link link, final StartupCommand[ try { final HostVO host = _resourceMgr.createHostVOForConnectedAgent(startup); if (host != null) { + checkHostArchOnCluster(host); ready = new ReadyCommand(host.getDataCenterId(), host.getId(), NumbersUtil.enableHumanReadableSizes); attache = sendReadyAndGetAttache(host, ready, link, startup); } @@ -1154,6 +1157,16 @@ private AgentAttache handleConnectedAgent(final Link link, final StartupCommand[ return attache; } + private void checkHostArchOnCluster(HostVO host) { + Cluster cluster = _resourceMgr.getCluster(host.getClusterId()); + if (cluster != null && !cluster.getArch().equals(host.getArch())) { + String msg = String.format("The host %s has arch %s and cannot be added to the %s cluster %s", + host.getName(), host.getArch().getType(), cluster.getArch().getType(), cluster.getName()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + protected class SimulateStartTask extends ManagedContextRunnable { ServerResource resource; Map details; diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineClusterVO.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineClusterVO.java index be35cea3c07e..c00d939b3dfd 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineClusterVO.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineClusterVO.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.engine.datacenter.entity.api.db; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.org.Cluster; import com.cloud.org.Grouping; @@ -26,6 +27,7 @@ import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity.State; import org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity.State.Event; +import org.apache.cloudstack.util.CPUArchConverter; import org.apache.cloudstack.util.HypervisorTypeConverter; import javax.persistence.Column; @@ -75,6 +77,10 @@ public class EngineClusterVO implements EngineCluster, Identity { @Enumerated(value = EnumType.STRING) AllocationState allocationState; + @Column(name = "arch") + @Convert(converter = CPUArchConverter.class) + private CPU.CPUArch arch; + @Column(name = "managed_state") @Enumerated(value = EnumType.STRING) ManagedState managedState; @@ -245,6 +251,15 @@ public State getState() { return state; } + @Override + public CPU.CPUArch getArch() { + return arch; + } + + public void setArch(CPU.CPUArch arch) { + this.arch = arch; + } + @Override public PartitionType partitionType() { return PartitionType.Cluster; diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java index f8535b6b6cc7..d804f079e174 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java @@ -38,6 +38,7 @@ import javax.persistence.TemporalType; import javax.persistence.Transient; +import com.cloud.cpu.CPU; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity.State; import org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity.State.Event; @@ -50,6 +51,7 @@ import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.StateMachine; +import org.apache.cloudstack.util.CPUArchConverter; import org.apache.cloudstack.util.HypervisorTypeConverter; @Entity @@ -122,6 +124,10 @@ public class EngineHostVO implements EngineHost, Identity { @Convert(converter = HypervisorTypeConverter.class) private HypervisorType hypervisorType; + @Column(name = "arch") + @Convert(converter = CPUArchConverter.class) + private CPU.CPUArch arch; + @Column(name = "proxy_port") private Integer proxyPort; @@ -725,6 +731,15 @@ public ResourceState getResourceState() { return resourceState; } + @Override + public CPU.CPUArch getArch() { + return arch; + } + + public void setArch(CPU.CPUArch arch) { + this.arch = arch; + } + public void setResourceState(ResourceState state) { resourceState = state; } 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 ce4c6bab94a3..e1b798d16d60 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 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.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 void setDhcpProviders(final List dhcpProviders) { NicSecondaryIpDao _nicSecondaryIpDao; @Inject ClusterDao clusterDao; + @Inject + RoutedIpv4Manager routedIpv4Manager; protected StateMachine2 _stateMachine; ScheduledExecutorService _executor; @@ -548,27 +558,27 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { 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 void doInTransactionWithoutResult(final TransactionStatus status) { 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 void doInTransactionWithoutResult(final TransactionStatus status) { 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 void doInTransactionWithoutResult(final TransactionStatus status) { 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 void doInTransactionWithoutResult(final TransactionStatus status) { 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 void implementNetworkElementsAndResources(final DeployDestination dest, f // 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; @@ -1634,7 +1645,7 @@ public void implementNetworkElementsAndResources(final DeployDestination dest, f if (ips.isEmpty()) { final Vpc vpc = _vpcMgr.getActiveVpc(network.getVpcId()); logger.debug("Creating a source nat ip for vpc {}", vpc); - _vpcMgr.assignSourceNatIpAddressToVpc(owner, vpc); + _vpcMgr.assignSourceNatIpAddressToVpc(owner, vpc, null); } } else { ips = _ipAddressDao.listByAssociatedNetwork(network.getId(), true); @@ -1742,6 +1753,13 @@ protected boolean reprogramNetworkRules(final long networkId, final Account call 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 Network createPrivateNetwork(final long networkOfferingId, final String n 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 Network createGuestNetwork(final long networkOfferingId, final String nam 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 @@ private Network createGuestNetwork(final long networkOfferingId, final String na 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 @@ private Network createGuestNetwork(final long networkOfferingId, final String na 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 Network doInTransaction(final TransactionStatus status) { 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 List doInTransaction(TransactionStatus status) { } 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 List doInTransaction(TransactionStatus status) { 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 @@ private boolean cleanupNetworkResources(final long networkId, final Account call 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/pom.xml b/engine/schema/pom.xml index d3b1f63ca943..82120ae70cc4 100644 --- a/engine/schema/pom.xml +++ b/engine/schema/pom.xml @@ -101,11 +101,11 @@ def csVersion = project.properties.getProperty('cs.version') def patch = project.properties.getProperty('patch.version') def templateList = [] - templateList.add("systemvmtemplate-${csVersion}.${patch}-kvm") - templateList.add("systemvmtemplate-${csVersion}.${patch}-vmware") - templateList.add("systemvmtemplate-${csVersion}.${patch}-xen") - templateList.add("systemvmtemplate-${csVersion}.${patch}-ovm") - templateList.add("systemvmtemplate-${csVersion}.${patch}-hyperv") + templateList.add("systemvmtemplate-${csVersion}.${patch}-x86_64-kvm") + templateList.add("systemvmtemplate-${csVersion}.${patch}-x86_64-vmware") + templateList.add("systemvmtemplate-${csVersion}.${patch}-x86_64-xen") + templateList.add("systemvmtemplate-${csVersion}.${patch}-x86_64-ovm") + templateList.add("systemvmtemplate-${csVersion}.${patch}-x86_64-hyperv") File file = new File("./engine/schema/dist/systemvm-templates/md5sum.txt") def lines = file.readLines() for (template in templateList) { @@ -194,7 +194,7 @@ true - ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-kvm.qcow2.bz2 + ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-kvm.qcow2.bz2 ${basedir}/dist/systemvm-templates/ ${kvm.checksum} @@ -230,7 +230,7 @@ true - ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-vmware.ova + ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-vmware.ova ${basedir}/dist/systemvm-templates/ ${vmware.checksum} @@ -266,7 +266,7 @@ true - ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-xen.vhd.bz2 + ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-xen.vhd.bz2 ${basedir}/dist/systemvm-templates/ ${xen.checksum} @@ -302,7 +302,7 @@ true - ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-ovm.raw.bz2 + ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-ovm.raw.bz2 ${basedir}/dist/systemvm-templates/ ${ovm.checksum} @@ -338,7 +338,7 @@ true - ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-hyperv.vhd.zip + ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-hyperv.vhd.zip ${basedir}/dist/systemvm-templates/ ${hyperv.checksum} 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 000000000000..3790213b3ade --- /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 000000000000..529d1cfb5fe8 --- /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/ClusterVO.java b/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java index 90591690eb0d..434901ef5b3b 100644 --- a/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java @@ -16,12 +16,14 @@ // under the License. package com.cloud.dc; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.org.Cluster; import com.cloud.org.Grouping; import com.cloud.org.Managed.ManagedState; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.util.CPUArchConverter; import org.apache.cloudstack.util.HypervisorTypeConverter; import javax.persistence.Column; @@ -69,6 +71,10 @@ public class ClusterVO implements Cluster { @Enumerated(value = EnumType.STRING) AllocationState allocationState; + @Column(name = "arch") + @Convert(converter = CPUArchConverter.class) + private String arch; + @Column(name = "managed_state") @Enumerated(value = EnumType.STRING) ManagedState managedState; @@ -200,6 +206,15 @@ public PartitionType partitionType() { return PartitionType.Cluster; } + @Override + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + + public void setArch(String arch) { + this.arch = arch; + } + @Override public String toString() { return String.format("Cluster {id: \"%s\", name: \"%s\", uuid: \"%s\"}", id, name, uuid); 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 000000000000..192f6bbaf315 --- /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 000000000000..1d2adf4d424d --- /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/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/util/LoggerFactory.java b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDao.java similarity index 75% rename from services/console-proxy/server/src/main/java/com/cloud/consoleproxy/util/LoggerFactory.java rename to engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDao.java index 121411adf162..3309a6f5fe54 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/util/LoggerFactory.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ASNumberRangeDao.java @@ -14,8 +14,14 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.consoleproxy.util; +package com.cloud.dc.dao; -public interface LoggerFactory { - Logger getLogger(Class clazz); +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 000000000000..4a4170685dc4 --- /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/dc/dao/ClusterDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java index ab9c5cab8c4a..6ecfdaeb0584 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.dc.dao; +import com.cloud.cpu.CPU; import com.cloud.dc.ClusterVO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.utils.db.GenericDao; @@ -50,4 +51,8 @@ public interface ClusterDao extends GenericDao { List listAllClusters(Long zoneId); boolean getSupportsResigning(long clusterId); + + List getClustersArchsByZone(long zoneId); + + List listClustersByArchAndZoneId(long zoneId, CPU.CPUArch arch); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java index 4d9bedba9669..9a56f0f2d949 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.dc.dao; +import com.cloud.cpu.CPU; import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.ClusterVO; @@ -43,6 +44,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; @Component public class ClusterDaoImpl extends GenericDaoBase implements ClusterDao { @@ -54,6 +56,8 @@ public class ClusterDaoImpl extends GenericDaoBase implements C protected final SearchBuilder ZoneHyTypeSearch; protected final SearchBuilder ZoneClusterSearch; protected final SearchBuilder ClusterSearch; + protected final SearchBuilder ClusterDistinctArchSearch; + protected final SearchBuilder ClusterArchSearch; protected GenericSearchBuilder ClusterIdSearch; @@ -104,6 +108,16 @@ public ClusterDaoImpl() { ClusterSearch = createSearchBuilder(); ClusterSearch.select(null, Func.DISTINCT, ClusterSearch.entity().getHypervisorType()); ClusterIdSearch.done(); + + ClusterDistinctArchSearch = createSearchBuilder(); + ClusterDistinctArchSearch.and("dataCenterId", ClusterDistinctArchSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + ClusterDistinctArchSearch.select(null, Func.DISTINCT, ClusterDistinctArchSearch.entity().getArch()); + ClusterDistinctArchSearch.done(); + + ClusterArchSearch = createSearchBuilder(); + ClusterArchSearch.and("dataCenterId", ClusterArchSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + ClusterArchSearch.and("arch", ClusterArchSearch.entity().getArch(), SearchCriteria.Op.EQ); + ClusterArchSearch.done(); } @Override @@ -301,4 +315,20 @@ public boolean getSupportsResigning(long clusterId) { return false; } + + @Override + public List getClustersArchsByZone(long zoneId) { + SearchCriteria sc = ClusterDistinctArchSearch.create(); + sc.setParameters("dataCenterId", zoneId); + List clusters = listBy(sc); + return clusters.stream().map(ClusterVO::getArch).collect(Collectors.toList()); + } + + @Override + public List listClustersByArchAndZoneId(long zoneId, CPU.CPUArch arch) { + SearchCriteria sc = ClusterArchSearch.create(); + sc.setParameters("dataCenterId", zoneId); + sc.setParameters("arch", arch); + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/host/HostVO.java b/engine/schema/src/main/java/com/cloud/host/HostVO.java index 1a507da79570..b5b634a73a70 100644 --- a/engine/schema/src/main/java/com/cloud/host/HostVO.java +++ b/engine/schema/src/main/java/com/cloud/host/HostVO.java @@ -42,6 +42,8 @@ import javax.persistence.TemporalType; import javax.persistence.Transient; +import com.cloud.cpu.CPU; +import org.apache.cloudstack.util.CPUArchConverter; import org.apache.cloudstack.util.HypervisorTypeConverter; import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @@ -153,6 +155,10 @@ public class HostVO implements Host { @Column(name = "hypervisor_version") private String hypervisorVersion; + @Column(name = "arch") + @Convert(converter = CPUArchConverter.class) + private CPU.CPUArch arch; + @Column(name = "update_count", updatable = true, nullable = false) protected long updated; // This field should be updated everytime the state is updated. There's no set method in the vo object because it is done with in the dao code. @@ -738,6 +744,15 @@ public ResourceState getResourceState() { return resourceState; } + @Override + public CPU.CPUArch getArch() { + return arch; + } + + public void setArch(CPU.CPUArch arch) { + this.arch = arch; + } + public void setResourceState(ResourceState state) { resourceState = state; } 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 21200dbf9b58..b80ccd9cd1b9 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 3ac860b08c5f..1698e0ed2da9 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 @@ protected FirewallRulesDaoImpl() { 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 void loadDestinationCidrs(FirewallRuleVO rule){ 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 e0509f80c2a4..1675c89811a5 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 @@ 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 31e083075fa9..f25bee5da471 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 boolean areServicesSupportedInNetwork(long networkId, Service... services return false; } + @Override + public boolean isAnyServiceSupportedInNetwork(long networkId, Provider provider, Service... services) { + SearchCriteria 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 581f78990691..02abaacd854e 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 String getGateway() { return gateway; } + @Override public void setGateway(String gateway) { this.gateway = gateway; } @@ -457,6 +461,7 @@ public String getCidr() { return cidr; } + @Override public void setCidr(String cidr) { this.cidr = cidr; } @@ -759,4 +764,13 @@ public Integer getPrivateMtu() { 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 350dda3f3b8b..41254ba4a8ba 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.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 void setForNsx(boolean forNsx) { 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 int getSortKey() { 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 c2024e06c51b..27d8227284b1 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 String getCidr() { 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 264a1ebc75e8..aa17723f0b17 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 1cc6a21da76c..b83fd8913059 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 @@ 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 boolean isIpv6Supported(long offeringId) { 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 b2fabf2e3cd3..0bf110757d7c 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 String getDisplayText() { @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 void setForNsx(boolean forNsx) { } @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 void setSupportsVmAutoScaling(boolean supportsVmAutoScaling) { 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 381d2144df16..abb63a10d065 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 823ea36b97f5..9bc74b139320 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 boolean isIpv6Supported(long offeringId) { NetUtils.InternetProtocol internetProtocol = getNetworkOfferingInternetProtocol(offeringId); return NetUtils.InternetProtocol.isIpv6EnabledProtocol(internetProtocol); } + + @Override + public boolean isRoutedNetwork(long offeringId) { + return NetworkOffering.NetworkMode.ROUTED.equals(findById(offeringId).getNetworkMode()); + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java b/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java index 25b02db64791..9dc9734f8ab3 100644 --- a/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java @@ -32,7 +32,9 @@ import javax.persistence.TemporalType; import javax.persistence.Transient; +import com.cloud.cpu.CPU; import com.cloud.user.UserData; +import org.apache.cloudstack.util.CPUArchConverter; import org.apache.cloudstack.util.HypervisorTypeConverter; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @@ -167,6 +169,10 @@ public class VMTemplateVO implements VirtualMachineTemplate { @Enumerated(value = EnumType.STRING) UserData.UserDataOverridePolicy userDataLinkPolicy; + @Column(name = "arch") + @Convert(converter = CPUArchConverter.class) + private CPU.CPUArch arch; + @Override public String getUniqueName() { return uniqueName; @@ -209,7 +215,7 @@ private VMTemplateVO(long id, String name, ImageFormat format, boolean isPublic, public VMTemplateVO(long id, String name, ImageFormat format, boolean isPublic, boolean featured, boolean isExtractable, TemplateType type, String url, boolean requiresHvm, int bits, long accountId, String cksum, String displayText, boolean enablePassword, long guestOSId, boolean bootable, HypervisorType hyperType, String templateTag, Map details, boolean sshKeyEnabled, boolean isDynamicallyScalable, boolean directDownload, - boolean deployAsIs) { + boolean deployAsIs, CPU.CPUArch arch) { this(id, name, format, @@ -235,6 +241,7 @@ public VMTemplateVO(long id, String name, ImageFormat format, boolean isPublic, state = State.Active; this.directDownload = directDownload; this.deployAsIs = deployAsIs; + this.arch = arch; } public static VMTemplateVO createPreHostIso(Long id, String uniqueName, String name, ImageFormat format, boolean isPublic, boolean featured, TemplateType type, @@ -673,4 +680,13 @@ public void setUserDataLinkPolicy(UserData.UserDataOverridePolicy userDataLinkPo this.userDataLinkPolicy = userDataLinkPolicy; } + @Override + public CPU.CPUArch getArch() { + return arch; + } + + public void setArch(CPU.CPUArch arch) { + this.arch = arch; + } + } diff --git a/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java b/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java index e12859ea8d6c..c105acf40b8d 100644 --- a/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java @@ -182,6 +182,9 @@ public class VolumeVO implements Volume { @Column(name = "encrypt_format") private String encryptFormat; + @Column(name = "delete_protection") + private boolean deleteProtection; + // Real Constructor public VolumeVO(Type type, String name, long dcId, long domainId, @@ -678,4 +681,13 @@ public void setExternalUuid(String externalUuid) { public String getEncryptFormat() { return encryptFormat; } public void setEncryptFormat(String encryptFormat) { this.encryptFormat = encryptFormat; } + + @Override + public boolean isDeleteProtection() { + return deleteProtection; + } + + public void setDeleteProtection(boolean deleteProtection) { + this.deleteProtection = deleteProtection; + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java index 853a99982264..93e747662775 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java @@ -32,7 +32,6 @@ import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.Storage; import com.cloud.utils.db.Attribute; -import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -60,16 +59,6 @@ protected DiskOfferingDaoImpl() { _computeOnlyAttr = _allAttributes.get("computeOnly"); } - @Override - public List searchIncludingRemoved(SearchCriteria sc, final Filter filter, final Boolean lock, final boolean cache) { - return super.searchIncludingRemoved(sc, filter, lock, cache); - } - - @Override - public List customSearchIncludingRemoved(SearchCriteria sc, final Filter filter) { - return super.customSearchIncludingRemoved(sc, filter); - } - @Override protected List executeList(final String sql, final Object... params) { StringBuilder builder = new StringBuilder(sql); diff --git a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java index c9bbea00d085..b559107436de 100644 --- a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java @@ -167,10 +167,8 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject details; @@ -545,6 +543,14 @@ public boolean isDynamicallyScalable() { return dynamicallyScalable; } + public boolean isDeleteProtection() { + return deleteProtection; + } + + public void setDeleteProtection(boolean deleteProtection) { + this.deleteProtection = deleteProtection; + } + @Override public Class getEntityType() { return VirtualMachine.class; diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDao.java index 39c65866658f..7de543e69d31 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDao.java @@ -53,7 +53,11 @@ public interface UserVmDao extends GenericDao { * @param hostName TODO * @param instanceName */ - void updateVM(long id, String displayName, boolean enable, Long osTypeId, String userData, Long userDataId, String userDataDetails, boolean displayVm, boolean isDynamicallyScalable, String customId, String hostName, String instanceName); + void updateVM(long id, String displayName, boolean enable, Long osTypeId, + String userData, Long userDataId, String userDataDetails, + boolean displayVm, boolean isDynamicallyScalable, + boolean deleteProtection, String customId, String hostName, + String instanceName); List findDestroyedVms(Date date); diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java index 536779125e24..cc8b9fc59a8d 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java @@ -274,8 +274,11 @@ public List listByAccountAndDataCenter(long accountId, long dcId) { } @Override - public void updateVM(long id, String displayName, boolean enable, Long osTypeId, String userData, Long userDataId, String userDataDetails, boolean displayVm, - boolean isDynamicallyScalable, String customId, String hostName, String instanceName) { + public void updateVM(long id, String displayName, boolean enable, Long osTypeId, + String userData, Long userDataId, String userDataDetails, + boolean displayVm, boolean isDynamicallyScalable, + boolean deleteProtection, String customId, String hostName, + String instanceName) { UserVmVO vo = createForUpdate(); vo.setDisplayName(displayName); vo.setHaEnabled(enable); @@ -285,6 +288,7 @@ public void updateVM(long id, String displayName, boolean enable, Long osTypeId, vo.setUserDataDetails(userDataDetails); vo.setDisplayVm(displayVm); vo.setDynamicallyScalable(isDynamicallyScalable); + vo.setDeleteProtection(deleteProtection); if (hostName != null) { vo.setHostName(hostName); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/datacenter/DataCenterIpv4GuestSubnetVO.java b/engine/schema/src/main/java/org/apache/cloudstack/datacenter/DataCenterIpv4GuestSubnetVO.java new file mode 100644 index 000000000000..828e7b39e9a4 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/datacenter/DataCenterIpv4GuestSubnetVO.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.datacenter; + +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 = "dc_ip4_guest_subnets") +public class DataCenterIpv4GuestSubnetVO implements DataCenterIpv4GuestSubnet { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + String uuid; + + @Column(name = "data_center_id") + private long dataCenterId; + + @Column(name = "subnet") + private String subnet; + + @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; + + public DataCenterIpv4GuestSubnetVO(long dcId, String subnet) { + this(); + this.dataCenterId = dcId; + this.subnet = subnet; + this.created = new Date(); + } + + protected DataCenterIpv4GuestSubnetVO() { + this.uuid = UUID.randomUUID().toString(); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public Long getDataCenterId() { + return dataCenterId; + } + + public void setDataCenterId(long dcId) { + this.dataCenterId = dcId; + } + + public String getSubnet() { + return subnet; + } + + public void setSubnet(String subnet) { + this.subnet = subnet; + } + + @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; + } + + +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/datacenter/dao/DataCenterIpv4GuestSubnetDao.java b/engine/schema/src/main/java/org/apache/cloudstack/datacenter/dao/DataCenterIpv4GuestSubnetDao.java new file mode 100644 index 000000000000..e231b267fda9 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/datacenter/dao/DataCenterIpv4GuestSubnetDao.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.datacenter.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnetVO; + +import java.util.List; + +public interface DataCenterIpv4GuestSubnetDao extends GenericDao { + + 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 000000000000..49e8a6ef6626 --- /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 000000000000..9e3378870113 --- /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 000000000000..b520ecd5cd1a --- /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 000000000000..0203b34fb1e2 --- /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 000000000000..cc726ba3d357 --- /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 000000000000..8ca4c2d86da7 --- /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 000000000000..0f95f7c3cd58 --- /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 000000000000..377bc45ebfe2 --- /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 000000000000..a974cf5e2769 --- /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 000000000000..8d8ec8c998a4 --- /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 000000000000..a5e5f47684a6 --- /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 000000000000..c3f860009db9 --- /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 000000000000..95e53448907e --- /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/java/org/apache/cloudstack/resourcedetail/FirewallRuleDetailVO.java b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/FirewallRuleDetailVO.java index 636d889fafe8..1149d0b13e77 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/FirewallRuleDetailVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/FirewallRuleDetailVO.java @@ -79,4 +79,8 @@ public long getResourceId() { public boolean isDisplay() { return display; } + + public void setValue(String value) { + this.value = value; + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/util/CPUArchConverter.java b/engine/schema/src/main/java/org/apache/cloudstack/util/CPUArchConverter.java new file mode 100644 index 000000000000..e278809fb96b --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/util/CPUArchConverter.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.util; + +import com.cloud.cpu.CPU; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +@Converter +public class CPUArchConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(CPU.CPUArch cpuArch) { + return cpuArch == null ? CPU.CPUArch.amd64.getType() : cpuArch.getType(); + } + + @Override + public CPU.CPUArch convertToEntityAttribute(String attribute) { + return CPU.CPUArch.fromType(attribute); + } +} 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 0be1e6100098..171685ce413e 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 2e7c9f9ba240..44fa9d94b83a 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', @@ -475,6 +599,11 @@ UPDATE `cloud`.`vm_instance` vm INNER JOIN `cloud`.`backup_offering` bo ON vm.ba -- CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.roles','state', 'varchar(10) NOT NULL default "enabled" COMMENT "role state"'); +-- Multi-Arch Zones +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.cluster', 'arch', 'varchar(8) DEFAULT "x86_64" COMMENT "the CPU architecture of the hosts in the cluster"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host', 'arch', 'varchar(8) DEFAULT "x86_64" COMMENT "the CPU architecture of the host"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_template', 'arch', 'varchar(8) DEFAULT "x86_64" COMMENT "the CPU architecture of the template/ISO"'); + -- NAS B&R Plugin Backup Repository DROP TABLE IF EXISTS `cloud`.`backup_repository`; CREATE TABLE `cloud`.`backup_repository` ( @@ -509,3 +638,6 @@ INSERT IGNORE INTO `cloud`.`hypervisor_capabilities` (uuid, hypervisor_type, hyp INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid, hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) SELECT UUID(),'VMware', '8.0.2', guest_os_name, guest_os_id, utc_timestamp(), 0 FROM `cloud`.`guest_os_hypervisor` WHERE hypervisor_type='VMware' AND hypervisor_version='8.0'; INSERT IGNORE INTO `cloud`.`hypervisor_capabilities` (uuid, hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled, max_data_volumes_limit, max_hosts_per_cluster, storage_motion_supported, vm_snapshot_enabled) values (UUID(), 'VMware', '8.0.3', 1024, 0, 59, 64, 1, 1); INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid, hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) SELECT UUID(),'VMware', '8.0.3', guest_os_name, guest_os_id, utc_timestamp(), 0 FROM `cloud`.`guest_os_hypervisor` WHERE hypervisor_type='VMware' AND hypervisor_version='8.0'; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_instance', 'delete_protection', 'boolean DEFAULT FALSE COMMENT "delete protection for vm" '); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'delete_protection', 'boolean DEFAULT FALSE COMMENT "delete protection for volumes" '); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql index 7bd4b3cc4a91..6fc8fb803862 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql @@ -41,6 +41,7 @@ SELECT host.cpus, host.speed, host.ram, + host.arch, cluster.id cluster_id, cluster.uuid cluster_uuid, cluster.name cluster_name, 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 bae73deda329..b6abaabcd48f 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.template_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql index 40b416b16de4..339e43860d88 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql @@ -50,6 +50,7 @@ SELECT `vm_template`.`sort_key` AS `sort_key`, `vm_template`.`removed` AS `removed`, `vm_template`.`enable_sshkey` AS `enable_sshkey`, + `vm_template`.`arch` AS `arch`, `parent_template`.`id` AS `parent_template_id`, `parent_template`.`uuid` AS `parent_template_uuid`, `source_template`.`id` AS `source_template_id`, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql index de48272006dc..97cb7b735cfc 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql @@ -54,6 +54,7 @@ SELECT `vm_instance`.`instance_name` AS `instance_name`, `vm_instance`.`guest_os_id` AS `guest_os_id`, `vm_instance`.`display_vm` AS `display_vm`, + `vm_instance`.`delete_protection` AS `delete_protection`, `guest_os`.`uuid` AS `guest_os_uuid`, `vm_instance`.`pod_id` AS `pod_id`, `host_pod_ref`.`uuid` AS `pod_uuid`, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql index 950dcddf4c71..ffeb93e8fa7a 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql @@ -40,6 +40,7 @@ SELECT `volumes`.`chain_info` AS `chain_info`, `volumes`.`external_uuid` AS `external_uuid`, `volumes`.`encrypt_format` AS `encrypt_format`, + `volumes`.`delete_protection` AS `delete_protection`, `account`.`id` AS `account_id`, `account`.`uuid` AS `account_uuid`, `account`.`account_name` AS `account_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 9aca869b510f..c74d50590de5 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/engine/schema/templateConfig.sh b/engine/schema/templateConfig.sh index fb12ad752a25..4205e06aa693 100644 --- a/engine/schema/templateConfig.sh +++ b/engine/schema/templateConfig.sh @@ -60,22 +60,23 @@ function createMetadataFile() { section="${template%%:*}" hvName=$(getGenericName $section) - templatename="systemvm-${section}-${VERSION}" - checksum=$(getChecksum "$fileData" "$VERSION-$hvName") downloadurl="${template#*:}" + arch=$(echo ${downloadurl#*"/systemvmtemplate-$VERSION-"} | cut -d'-' -f 1) + templatename="systemvm-${section%.*}-${VERSION}-${arch}" + checksum=$(getChecksum "$fileData" "$VERSION-${arch}-$hvName") filename=$(echo ${downloadurl##*'/'}) - echo -e "["$section"]\ntemplatename = $templatename\nchecksum = $checksum\ndownloadurl = $downloadurl\nfilename = $filename\n" >> $METADATAFILE + echo -e "["$section"]\ntemplatename = $templatename\nchecksum = $checksum\ndownloadurl = $downloadurl\nfilename = $filename\narch = $arch\n" >> $METADATAFILE done } declare -a templates getTemplateVersion $1 -templates=( "kvm:https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-$VERSION-kvm.qcow2.bz2" - "vmware:https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-$VERSION-vmware.ova" - "xenserver:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-xen.vhd.bz2" - "hyperv:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-hyperv.vhd.zip" - "lxc:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-kvm.qcow2.bz2" - "ovm3:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-ovm.raw.bz2" ) +templates=( "kvm:https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-$VERSION-x86_64-kvm.qcow2.bz2" + "vmware:https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-$VERSION-x86_64-vmware.ova" + "xenserver:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-x86_64-xen.vhd.bz2" + "hyperv:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-x86_64-hyperv.vhd.zip" + "lxc:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-x86_64-kvm.qcow2.bz2" + "ovm3:https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-x86_64-ovm.raw.bz2" ) PARENTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )/dist/systemvm-templates/" mkdir -p $PARENTPATH diff --git a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateServiceImpl.java b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateServiceImpl.java index 518a38698338..5e21f37f4d53 100644 --- a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateServiceImpl.java +++ b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateServiceImpl.java @@ -814,7 +814,7 @@ private boolean createChildDataDiskTemplate(DatadiskTO dataDiskTemplate, VMTempl String templateName = dataDiskTemplate.isIso() ? dataDiskTemplate.getPath().substring(dataDiskTemplate.getPath().lastIndexOf(File.separator) + 1) : template.getName() + suffix + diskCount; VMTemplateVO templateVO = new VMTemplateVO(templateId, templateName, format, false, false, false, ttype, template.getUrl(), template.requiresHvm(), template.getBits(), template.getAccountId(), null, templateName, false, guestOsId, false, template.getHypervisorType(), null, - null, false, false, false, false); + null, false, false, false, false, template.getArch()); if (dataDiskTemplate.isIso()){ templateVO.setUniqueName(templateName); } diff --git a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/store/TemplateObject.java b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/store/TemplateObject.java index 37ddc3573c11..b7d83c702231 100644 --- a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/store/TemplateObject.java +++ b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/store/TemplateObject.java @@ -23,6 +23,7 @@ import javax.inject.Inject; +import com.cloud.cpu.CPU; import com.cloud.storage.StorageManager; import com.cloud.user.UserData; import org.apache.logging.log4j.Logger; @@ -350,6 +351,11 @@ public UserData.UserDataOverridePolicy getUserDataOverridePolicy() { return imageVO.getUserDataOverridePolicy(); } + @Override + public CPU.CPUArch getArch() { + return imageVO.getArch(); + } + @Override public DataTO getTO() { DataTO to = null; diff --git a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeObject.java index 1b3bec0e9075..825a8cbd941c 100644 --- a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeObject.java +++ b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeObject.java @@ -935,6 +935,11 @@ public void setEncryptFormat(String encryptFormat) { volumeVO.setEncryptFormat(encryptFormat); } + @Override + public boolean isDeleteProtection() { + return volumeVO.isDeleteProtection(); + } + @Override public boolean isFollowRedirects() { return followRedirects; diff --git a/plugins/affinity-group-processors/host-affinity/src/main/java/org/apache/cloudstack/affinity/HostAffinityProcessor.java b/plugins/affinity-group-processors/host-affinity/src/main/java/org/apache/cloudstack/affinity/HostAffinityProcessor.java index 4a5bbe8e787a..b94cf49e4d94 100644 --- a/plugins/affinity-group-processors/host-affinity/src/main/java/org/apache/cloudstack/affinity/HostAffinityProcessor.java +++ b/plugins/affinity-group-processors/host-affinity/src/main/java/org/apache/cloudstack/affinity/HostAffinityProcessor.java @@ -63,7 +63,9 @@ public void process(VirtualMachineProfile vmProfile, DeploymentPlan plan, Exclud Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { - _affinityGroupDao.listByIds(affinityGroupIdList, true); + if (!affinityGroupIdList.isEmpty()) { + _affinityGroupDao.listByIds(affinityGroupIdList, true); + } for (AffinityGroupVMMapVO vmGroupMapping : vmGroupMappings) { processAffinityGroup(vmGroupMapping, plan, vm, vmList); } @@ -149,7 +151,9 @@ public boolean check(VirtualMachineProfile vmProfile, DeployDestination plannedD return Transaction.execute(new TransactionCallback() { @Override public Boolean doInTransaction(TransactionStatus status) { - _affinityGroupDao.listByIds(affinityGroupIds, true); + if (!affinityGroupIds.isEmpty()) { + _affinityGroupDao.listByIds(affinityGroupIds, true); + } for (AffinityGroupVMMapVO vmGroupMapping : vmGroupMappings) { if (!checkAffinityGroup(vmGroupMapping, vm, plannedHostId)) { return false; diff --git a/plugins/affinity-group-processors/host-anti-affinity/src/main/java/org/apache/cloudstack/affinity/HostAntiAffinityProcessor.java b/plugins/affinity-group-processors/host-anti-affinity/src/main/java/org/apache/cloudstack/affinity/HostAntiAffinityProcessor.java index e724f02fe98b..4681ce4321ee 100644 --- a/plugins/affinity-group-processors/host-anti-affinity/src/main/java/org/apache/cloudstack/affinity/HostAntiAffinityProcessor.java +++ b/plugins/affinity-group-processors/host-anti-affinity/src/main/java/org/apache/cloudstack/affinity/HostAntiAffinityProcessor.java @@ -78,7 +78,9 @@ public void process(VirtualMachineProfile vmProfile, DeploymentPlan plan, Exclud Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { - _affinityGroupDao.listByIds(affinityGroupIds, true); + if (!affinityGroupIds.isEmpty()) { + _affinityGroupDao.listByIds(affinityGroupIds, true); + } for (AffinityGroupVMMapVO vmGroupMapping : vmGroupMappings) { processAffinityGroup(vmGroupMapping, avoid, vm); } @@ -165,7 +167,9 @@ public boolean check(VirtualMachineProfile vmProfile, DeployDestination plannedD return Transaction.execute(new TransactionCallback() { @Override public Boolean doInTransaction(TransactionStatus status) { - _affinityGroupDao.listByIds(affinityGroupIds, true); + if (!affinityGroupIds.isEmpty()) { + _affinityGroupDao.listByIds(affinityGroupIds, true); + } for (AffinityGroupVMMapVO vmGroupMapping : vmGroupMappings) { // if more than 1 VM's are present in the group then check for // conflict due to parallel deployment diff --git a/plugins/dedicated-resources/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java b/plugins/dedicated-resources/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java index 9060eccb64a2..4f1db396b7c4 100644 --- a/plugins/dedicated-resources/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java +++ b/plugins/dedicated-resources/src/main/java/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java @@ -23,6 +23,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.commons.lang3.StringUtils; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupService; import org.apache.cloudstack.affinity.dao.AffinityGroupDao; @@ -236,7 +237,7 @@ public List dedicateZone(final Long zoneId, final Long doma @Override public List doInTransaction(TransactionStatus status) { // find or create the affinity group by name under this account/domain - AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal); + AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal, DedicatedResources.Type.Zone); if (group == null) { logger.error("Unable to dedicate zone due to, failed to create dedication affinity group"); throw new CloudRuntimeException("Failed to dedicate zone. Please contact Cloud Support."); @@ -372,10 +373,10 @@ public List dedicatePod(final Long podId, final Long domain @Override public List doInTransaction(TransactionStatus status) { // find or create the affinity group by name under this account/domain - AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal); + AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal, DedicatedResources.Type.Pod); if (group == null) { - logger.error("Unable to dedicate zone due to, failed to create dedication affinity group"); - throw new CloudRuntimeException("Failed to dedicate zone. Please contact Cloud Support."); + logger.error("Unable to dedicate pod due to, failed to create dedication affinity group"); + throw new CloudRuntimeException("Failed to dedicate pod. Please contact Cloud Support."); } DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(null, podId, null, null, null, null, group.getId()); try { @@ -485,10 +486,10 @@ public List dedicateCluster(final Long clusterId, final Lon @Override public List doInTransaction(TransactionStatus status) { // find or create the affinity group by name under this account/domain - AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal); + AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal, DedicatedResources.Type.Cluster); if (group == null) { - logger.error("Unable to dedicate zone due to, failed to create dedication affinity group"); - throw new CloudRuntimeException("Failed to dedicate zone. Please contact Cloud Support."); + logger.error("Unable to dedicate cluster due to, failed to create dedication affinity group"); + throw new CloudRuntimeException("Failed to dedicate cluster. Please contact Cloud Support."); } DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(null, null, clusterId, null, null, null, group.getId()); try { @@ -582,10 +583,10 @@ public List dedicateHost(final Long hostId, final Long doma @Override public List doInTransaction(TransactionStatus status) { // find or create the affinity group by name under this account/domain - AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal); + AffinityGroup group = findOrCreateDedicatedAffinityGroup(domainId, accountIdFinal, DedicatedResources.Type.Host); if (group == null) { - logger.error("Unable to dedicate zone due to, failed to create dedication affinity group"); - throw new CloudRuntimeException("Failed to dedicate zone. Please contact Cloud Support."); + logger.error("Unable to dedicate host due to, failed to create dedication affinity group"); + throw new CloudRuntimeException("Failed to dedicate host. Please contact Cloud Support."); } DedicatedResourceVO dedicatedResource = new DedicatedResourceVO(null, null, null, hostId, null, null, group.getId()); try { @@ -607,7 +608,7 @@ public List doInTransaction(TransactionStatus status) { } - private AffinityGroup findOrCreateDedicatedAffinityGroup(Long domainId, Long accountId) { + private AffinityGroup findOrCreateDedicatedAffinityGroup(Long domainId, Long accountId, DedicatedResources.Type dedicatedResource) { if (domainId == null) { return null; } @@ -624,24 +625,25 @@ private AffinityGroup findOrCreateDedicatedAffinityGroup(Long domainId, Long acc if (group != null) { return group; } - // default to a groupname with account/domain information - affinityGroupName = "DedicatedGrp-" + accountName; + // defaults to a groupName with resourceType and account/domain information + affinityGroupName = String.format("Dedicated%sGrp-%s", dedicatedResource, accountName); } else { // domain level group group = _affinityGroupDao.findDomainLevelGroupByType(domainId, "ExplicitDedication"); if (group != null) { return group; } - // default to a groupname with account/domain information + + // defaults to a groupName with resourceType and account/domain information String domainName = _domainDao.findById(domainId).getName(); - affinityGroupName = "DedicatedGrp-domain-" + domainName; + affinityGroupName = String.format("Dedicated%sGrp-domain-%s", dedicatedResource, domainName); } - group = _affinityGroupService.createAffinityGroup(accountName, null, domainId, affinityGroupName, "ExplicitDedication", "dedicated resources group"); + String description = String.format("Dedicated %s group", StringUtils.lowerCase(dedicatedResource.toString())); + group = _affinityGroupService.createAffinityGroup(accountName, null, domainId, affinityGroupName, "ExplicitDedication", description); return group; - } private List getVmsOnHost(long hostId) { diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index f3e379f2c842..abd6e24a5326 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -44,6 +44,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.naming.ConfigurationException; import javax.xml.parsers.DocumentBuilder; @@ -76,6 +78,8 @@ import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import org.apache.xerces.impl.xpath.regex.Match; @@ -446,7 +450,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv protected WatchDogModel watchDogModel = WatchDogModel.I6300ESB; private final Map pifs = new HashMap(); - private final Map vmStats = new ConcurrentHashMap(); + private final Map vmStats = new ConcurrentHashMap<>(); private final Map vmDiskStats = new ConcurrentHashMap<>(); @@ -2346,7 +2350,7 @@ public PowerState getVmState(final Connect conn, final String vmName) { final PowerState s = convertToPowerState(vms.getInfo().state); return s; } catch (final LibvirtException e) { - LOGGER.warn("Can't get vm state " + vmName + e.getMessage() + "retry:" + retry); + LOGGER.error("Could not get state for VM [{}] (retry={}) due to:", vmName, retry, e); } finally { try { if (vms != null) { @@ -3655,7 +3659,6 @@ public StartupCommand[] initialize() { if (dpdkSupport) { capabilities += ",dpdk"; } - final StartupRoutingCommand cmd = new StartupRoutingCommand(info.getAllocatableCpus(), info.getCpuSpeed(), info.getTotalMemory(), info.getReservedMemory(), capabilities, hypervisorType, RouterPrivateIpStrategy.HostLocal); @@ -3675,6 +3678,9 @@ public StartupCommand[] initialize() { if (healthCheckResult != HealthCheckResult.IGNORE) { cmd.setHostHealthCheckResult(healthCheckResult == HealthCheckResult.SUCCESS); } + if (StringUtils.isNotBlank(info.getCpuArch())) { + cmd.setCpuArch(info.getCpuArch()); + } if (cmd.getHostDetails().containsKey("Host.OS")) { hostDistro = cmd.getHostDetails().get("Host.OS"); @@ -4412,127 +4418,189 @@ protected String getDiskPathFromDiskDef(DiskDef disk) { return null; } - private class VmStats { - long usedTime; - long tx; - long rx; - long ioRead; - long ioWrote; - long bytesRead; - long bytesWrote; - Calendar timestamp; + private String vmToString(Domain dm) throws LibvirtException { + return String.format("{\"name\":\"%s\",\"uuid\":\"%s\"}", dm.getName(), dm.getUUIDString()); } + /** + * Returns metrics for the period since this function was last called for the specified VM. + * @param conn the Libvirt connection. + * @param vmName name of the VM. + * @return metrics for the period since last time this function was called for the VM. + * @throws LibvirtException + */ public VmStatsEntry getVmStat(final Connect conn, final String vmName) throws LibvirtException { Domain dm = null; try { + LOGGER.debug("Trying to get VM with name [{}].", vmName); dm = getDomain(conn, vmName); if (dm == null) { + LOGGER.warn("Could not get VM with name [{}].", vmName); return null; } - DomainInfo info = dm.getInfo(); - final VmStatsEntry stats = new VmStatsEntry(); - stats.setNumCPUs(info.nrVirtCpu); - stats.setEntityType("vm"); + LibvirtExtendedVmStatsEntry newStats = getVmCurrentStats(dm); + LibvirtExtendedVmStatsEntry oldStats = vmStats.get(vmName); - stats.setMemoryKBs(info.maxMem); - stats.setTargetMemoryKBs(info.memory); - stats.setIntFreeMemoryKBs(getMemoryFreeInKBs(dm)); + VmStatsEntry metrics = calculateVmMetrics(dm, oldStats, newStats); + vmStats.put(vmName, newStats); - /* get cpu utilization */ - VmStats oldStats = null; + return metrics; + } finally { + if (dm != null) { + dm.free(); + } + } + } - final Calendar now = Calendar.getInstance(); + /** + * Returns a VM's current statistics. + * @param dm domain of the VM. + * @return current statistics of the VM. + * @throws LibvirtException + */ + protected LibvirtExtendedVmStatsEntry getVmCurrentStats(final Domain dm) throws LibvirtException { + final LibvirtExtendedVmStatsEntry stats = new LibvirtExtendedVmStatsEntry(); - oldStats = vmStats.get(vmName); + getVmCurrentCpuStats(dm, stats); + getVmCurrentNetworkStats(dm, stats); + getVmCurrentDiskStats(dm, stats); - long elapsedTime = 0; - if (oldStats != null) { - elapsedTime = now.getTimeInMillis() - oldStats.timestamp.getTimeInMillis(); - double utilization = (info.cpuTime - oldStats.usedTime) / ((double)elapsedTime * 1000000); + LOGGER.debug("Retrieved statistics for VM [{}]: [{}].", vmToString(dm), stats); + stats.setTimestamp(Calendar.getInstance()); + return stats; + } - utilization = utilization / info.nrVirtCpu; - if (utilization > 0) { - stats.setCPUUtilization(utilization * 100); - } - } + /** + * Passes a VM's current CPU statistics into the provided LibvirtExtendedVmStatsEntry. + * @param dm domain of the VM. + * @param stats LibvirtExtendedVmStatsEntry that will receive the current CPU statistics. + * @throws LibvirtException + */ + protected void getVmCurrentCpuStats(final Domain dm, final LibvirtExtendedVmStatsEntry stats) throws LibvirtException { + LOGGER.trace("Getting CPU stats for VM [{}].", vmToString(dm)); + stats.setCpuTime(dm.getInfo().cpuTime); + } - /* get network stats */ + /** + * Passes a VM's current network statistics into the provided LibvirtExtendedVmStatsEntry. + * @param dm domain of the VM. + * @param stats LibvirtExtendedVmStatsEntry that will receive the current network statistics. + * @throws LibvirtException + */ + protected void getVmCurrentNetworkStats(final Domain dm, final LibvirtExtendedVmStatsEntry stats) throws LibvirtException { + final String vmAsString = vmToString(dm); + LOGGER.trace("Getting network stats for VM [{}].", vmAsString); + final List vifs = getInterfaces(dm.getConnect(), dm.getName()); + LOGGER.debug("Found [{}] network interface(s) for VM [{}].", vifs.size(), vmAsString); + double rx = 0; + double tx = 0; + for (final InterfaceDef vif : vifs) { + final DomainInterfaceStats ifStats = dm.interfaceStats(vif.getDevName()); + rx += ifStats.rx_bytes; + tx += ifStats.tx_bytes; + } + stats.setNetworkReadKBs(rx / 1024); + stats.setNetworkWriteKBs(tx / 1024); + } - final List vifs = getInterfaces(conn, vmName); - long rx = 0; - long tx = 0; - for (final InterfaceDef vif : vifs) { - final DomainInterfaceStats ifStats = dm.interfaceStats(vif.getDevName()); - rx += ifStats.rx_bytes; - tx += ifStats.tx_bytes; + /** + * Passes a VM's current disk statistics into the provided LibvirtExtendedVmStatsEntry. + * @param dm domain of the VM. + * @param stats LibvirtExtendedVmStatsEntry that will receive the current disk statistics. + * @throws LibvirtException + */ + protected void getVmCurrentDiskStats(final Domain dm, final LibvirtExtendedVmStatsEntry stats) throws LibvirtException { + final String vmAsString = vmToString(dm); + LOGGER.trace("Getting disk stats for VM [{}].", vmAsString); + final List disks = getDisks(dm.getConnect(), dm.getName()); + LOGGER.debug("Found [{}] disk(s) for VM [{}].", disks.size(), vmAsString); + long io_rd = 0; + long io_wr = 0; + double bytes_rd = 0; + double bytes_wr = 0; + for (final DiskDef disk : disks) { + if (disk.getDeviceType() == DeviceType.CDROM || disk.getDeviceType() == DeviceType.FLOPPY) { + LOGGER.debug("Ignoring disk [{}] in VM [{}]'s stats since its deviceType is [{}].", disk.toString().replace("\n", ""), vmAsString, disk.getDeviceType()); + continue; } + final DomainBlockStats blockStats = dm.blockStats(disk.getDiskLabel()); + io_rd += blockStats.rd_req; + io_wr += blockStats.wr_req; + bytes_rd += blockStats.rd_bytes; + bytes_wr += blockStats.wr_bytes; + } + stats.setDiskReadIOs(io_rd); + stats.setDiskWriteIOs(io_wr); + stats.setDiskReadKBs(bytes_rd / 1024); + stats.setDiskWriteKBs(bytes_wr / 1024); + } - if (oldStats != null) { - final double deltarx = rx - oldStats.rx; - if (deltarx > 0) { - stats.setNetworkReadKBs(deltarx / 1024); - } - final double deltatx = tx - oldStats.tx; - if (deltatx > 0) { - stats.setNetworkWriteKBs(deltatx / 1024); - } + /** + * Calculates a VM's metrics for the period between the two statistics given as parameters. + * @param dm domain of the VM. + * @param oldStats old statistics. If null, the CPU, network and disk utilization won't be calculated. + * @param newStats new statistics. + * @return metrics for the period between the two statistics. + * @throws LibvirtException + */ + protected VmStatsEntry calculateVmMetrics(final Domain dm, final LibvirtExtendedVmStatsEntry oldStats, final LibvirtExtendedVmStatsEntry newStats) throws LibvirtException { + final VmStatsEntry metrics = new VmStatsEntry(); + final DomainInfo info = dm.getInfo(); + final String vmAsString = vmToString(dm); + + metrics.setEntityType("vm"); + LOGGER.trace("Writing VM [{}]'s CPU and memory information into the metrics.", vmAsString); + metrics.setNumCPUs(info.nrVirtCpu); + metrics.setMemoryKBs(info.maxMem); + metrics.setTargetMemoryKBs(info.memory); + LOGGER.trace("Trying to get free memory for VM [{}].", vmAsString); + metrics.setIntFreeMemoryKBs(getMemoryFreeInKBs(dm)); + + if (oldStats != null) { + LOGGER.debug("Old stats exist for VM [{}]; therefore, the utilization will be calculated.", vmAsString); + + LOGGER.trace("Calculating CPU utilization for VM [{}].", vmAsString); + final Calendar now = Calendar.getInstance(); + long elapsedTime = now.getTimeInMillis() - oldStats.getTimestamp().getTimeInMillis(); + double utilization = (info.cpuTime - oldStats.getCpuTime()) / ((double) elapsedTime * 1000000 * info.nrVirtCpu); + if (utilization > 0) { + metrics.setCPUUtilization(utilization * 100); } - /* get disk stats */ - final List disks = getDisks(conn, vmName); - long io_rd = 0; - long io_wr = 0; - long bytes_rd = 0; - long bytes_wr = 0; - for (final DiskDef disk : disks) { - if (disk.getDeviceType() == DeviceType.CDROM || disk.getDeviceType() == DeviceType.FLOPPY) { - continue; - } - final DomainBlockStats blockStats = dm.blockStats(disk.getDiskLabel()); - io_rd += blockStats.rd_req; - io_wr += blockStats.wr_req; - bytes_rd += blockStats.rd_bytes; - bytes_wr += blockStats.wr_bytes; + LOGGER.trace("Calculating network utilization for VM [{}].", vmAsString); + final double deltarx = newStats.getNetworkReadKBs() - oldStats.getNetworkReadKBs(); + if (deltarx > 0) { + metrics.setNetworkReadKBs(deltarx); } - - if (oldStats != null) { - final long deltaiord = io_rd - oldStats.ioRead; - if (deltaiord > 0) { - stats.setDiskReadIOs(deltaiord); - } - final long deltaiowr = io_wr - oldStats.ioWrote; - if (deltaiowr > 0) { - stats.setDiskWriteIOs(deltaiowr); - } - final double deltabytesrd = bytes_rd - oldStats.bytesRead; - if (deltabytesrd > 0) { - stats.setDiskReadKBs(deltabytesrd / 1024); - } - final double deltabyteswr = bytes_wr - oldStats.bytesWrote; - if (deltabyteswr > 0) { - stats.setDiskWriteKBs(deltabyteswr / 1024); - } + final double deltatx = newStats.getNetworkWriteKBs() - oldStats.getNetworkWriteKBs(); + if (deltatx > 0) { + metrics.setNetworkWriteKBs(deltatx); } - /* save to Hashmap */ - final VmStats newStat = new VmStats(); - newStat.usedTime = info.cpuTime; - newStat.rx = rx; - newStat.tx = tx; - newStat.ioRead = io_rd; - newStat.ioWrote = io_wr; - newStat.bytesRead = bytes_rd; - newStat.bytesWrote = bytes_wr; - newStat.timestamp = now; - vmStats.put(vmName, newStat); - return stats; - } finally { - if (dm != null) { - dm.free(); + LOGGER.trace("Calculating disk utilization for VM [{}].", vmAsString); + final double deltaiord = newStats.getDiskReadIOs() - oldStats.getDiskReadIOs(); + if (deltaiord > 0) { + metrics.setDiskReadIOs(deltaiord); + } + final double deltaiowr = newStats.getDiskWriteIOs() - oldStats.getDiskWriteIOs(); + if (deltaiowr > 0) { + metrics.setDiskWriteIOs(deltaiowr); + } + final double deltabytesrd = newStats.getDiskReadKBs() - oldStats.getDiskReadKBs(); + if (deltabytesrd > 0) { + metrics.setDiskReadKBs(deltabytesrd); + } + final double deltabyteswr = newStats.getDiskWriteKBs() - oldStats.getDiskWriteKBs(); + if (deltabyteswr > 0) { + metrics.setDiskWriteKBs(deltabyteswr); } } + + String metricsAsString = new ReflectionToStringBuilder(metrics, ToStringStyle.JSON_STYLE).setExcludeFieldNames("vmId", "vmUuid").toString(); + LOGGER.debug("Calculated metrics for VM [{}]: [{}].", vmAsString, metricsAsString); + + return metrics; } /** @@ -4544,10 +4612,8 @@ public VmStatsEntry getVmStat(final Connect conn, final String vmName) throws Li */ protected long getMemoryFreeInKBs(Domain dm) throws LibvirtException { MemoryStatistic[] memoryStats = dm.memoryStats(NUMMEMSTATS); - - if(LOGGER.isTraceEnabled()){ - LOGGER.trace(String.format("Retrieved memory statistics (information about tags can be found on the libvirt documentation):", ArrayUtils.toString(memoryStats))); - } + LOGGER.trace("Retrieved memory statistics (information about tags can be found on the libvirt documentation): {}.", + () -> Stream.of(memoryStats).map(stat -> stat.toString().trim().replace("\n", ",")).collect(Collectors.joining("},{", "[{", "}]"))); long freeMemory = NumberUtils.LONG_MINUS_ONE; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtExtendedVmStatsEntry.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtExtendedVmStatsEntry.java new file mode 100644 index 000000000000..8e0e4a2ae274 --- /dev/null +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtExtendedVmStatsEntry.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 com.cloud.hypervisor.kvm.resource; + +import com.cloud.agent.api.VmStatsEntry; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import java.util.Calendar; + +public class LibvirtExtendedVmStatsEntry extends VmStatsEntry { + private long cpuTime; + private Calendar timestamp; + + public LibvirtExtendedVmStatsEntry() { + } + + public long getCpuTime() { + return cpuTime; + } + + public void setCpuTime(long cpuTime) { + this.cpuTime = cpuTime; + } + + public Calendar getTimestamp() { + return timestamp; + } + + public void setTimestamp(Calendar timestamp) { + this.timestamp = timestamp; + } + + @Override + public String toString() { + return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "cpuTime", "networkWriteKBs", "networkReadKBs", "diskReadIOs", "diskWriteIOs", "diskReadKBs", "diskWriteKBs"); + } +} diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java index c9abf399530a..9d9a6415e27f 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java @@ -18,6 +18,7 @@ import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.apache.cloudstack.utils.qemu.QemuObject; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; @@ -25,8 +26,10 @@ public class KVMPhysicalDisk { private String path; - private String name; - private KVMStoragePool pool; + private final String name; + private final KVMStoragePool pool; + private String dispName; + private String vmName; private boolean useAsTemplate; public static String RBDStringBuilder(String monHost, int monPort, String authUserName, String authSecret, String image) { @@ -81,7 +84,9 @@ public KVMPhysicalDisk(String path, String name, KVMStoragePool pool) { @Override public String toString() { - return "KVMPhysicalDisk [path=" + path + ", name=" + name + ", pool=" + pool + ", format=" + format + ", size=" + size + ", virtualSize=" + virtualSize + "]"; + return String.format("KVMPhysicalDisk %s", + ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "path", "name", "pool", "format", "size", "virtualSize", "dispName", "vmName")); } public void setFormat(PhysicalDiskFormat format) { @@ -135,4 +140,20 @@ public void setQemuEncryptFormat(QemuObject.EncryptFormat format) { public void setUseAsTemplate() { this.useAsTemplate = true; } public boolean useAsTemplate() { return this.useAsTemplate; } + + public String getDispName() { + return dispName; + } + + public void setDispName(String dispName) { + this.dispName = dispName; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index f6242444d911..e51f8fc81529 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -435,6 +435,7 @@ public Answer cloneVolumeFromBaseTemplate(final CopyCommand cmd) { if (!storagePoolMgr.connectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), path, details)) { logger.warn("Failed to connect new volume at path: " + path + ", in storage pool id: " + primaryStore.getUuid()); } + BaseVol.setDispName(template.getName()); vol = storagePoolMgr.copyPhysicalDisk(BaseVol, path != null ? path : volume.getUuid(), primaryPool, cmd.getWaitInMillSeconds(), null, volume.getPassphrase(), volume.getProvisioningType()); @@ -527,6 +528,8 @@ public Answer copyVolumeFromImageCacheToPrimary(final CopyCommand cmd) { final KVMPhysicalDisk volume = secondaryStoragePool.getPhysicalDisk(srcVolumeName); volume.setFormat(PhysicalDiskFormat.valueOf(srcFormat.toString())); + volume.setDispName(srcVol.getName()); + volume.setVmName(srcVol.getVmName()); final KVMPhysicalDisk newDisk = storagePoolMgr.copyPhysicalDisk(volume, path != null ? path : volumeName, primaryPool, cmd.getWaitInMillSeconds()); @@ -2492,6 +2495,8 @@ public Answer copyVolumeFromPrimaryToPrimary(CopyCommand cmd) { } volume.setFormat(PhysicalDiskFormat.valueOf(srcFormat.toString())); + volume.setDispName(srcVol.getName()); + volume.setVmName(srcVol.getVmName()); String destVolumeName = null; if (destPrimaryStore.isManaged()) { diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index c0f6e44cddd9..45a65037340d 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -1409,7 +1409,10 @@ public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMSt */ KVMStoragePool srcPool = disk.getPool(); - PhysicalDiskFormat sourceFormat = disk.getFormat(); + /* Linstor images are always stored as RAW, but Linstor uses qcow2 in DB, + to support snapshots(backuped) as qcow2 files. */ + PhysicalDiskFormat sourceFormat = srcPool.getType() != StoragePoolType.Linstor ? + disk.getFormat() : PhysicalDiskFormat.RAW; String sourcePath = disk.getPath(); KVMPhysicalDisk newDisk; diff --git a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java index 4293ee75f7ef..c0b416410cbc 100644 --- a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java +++ b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java @@ -53,12 +53,14 @@ public class KVMHostInfo { private int allocatableCpus; private int cpusockets; private long cpuSpeed; + private String cpuArch; private long totalMemory; private long reservedMemory; private long overCommitMemory; private List capabilities = new ArrayList<>(); private static String cpuInfoFreqFileName = "/sys/devices/system/cpu/cpu0/cpufreq/base_frequency"; + private static String cpuArchCommand = "/usr/bin/arch"; public KVMHostInfo(long reservedMemory, long overCommitMemory, long manualSpeed, int reservedCpus) { this.cpuSpeed = manualSpeed; @@ -105,6 +107,10 @@ public List getCapabilities() { return this.capabilities; } + public String getCpuArch() { + return cpuArch; + } + protected static long getCpuSpeed(final String cpabilities, final NodeInfo nodeInfo) { long speed = 0L; speed = getCpuSpeedFromCommandLscpu(); @@ -201,6 +207,7 @@ private void getHostInfoFromLibvirt() { this.cpusockets = hosts.sockets * hosts.nodes; } this.totalCpus = hosts.cpus; + this.cpuArch = getCPUArchFromCommand(); final LibvirtCapXMLParser parser = new LibvirtCapXMLParser(); parser.parseCapabilitiesXML(capabilities); @@ -227,4 +234,9 @@ private void getHostInfoFromLibvirt() { LOGGER.error("Caught libvirt exception while fetching host information", e); } } + + private String getCPUArchFromCommand() { + LOGGER.info("Fetching host CPU arch"); + return Script.runSimpleBashScript(cpuArchCommand); + } } diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index ecb34adc6eda..30d0b2ab1635 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -82,6 +82,7 @@ import org.libvirt.DomainInterfaceStats; import org.libvirt.LibvirtException; import org.libvirt.MemoryStatistic; +import org.libvirt.NodeInfo; import org.libvirt.SchedUlongParameter; import org.libvirt.StorageVol; import org.libvirt.VcpuInfo; @@ -93,7 +94,6 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.Spy; -import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.MockitoJUnitRunner; import org.w3c.dom.Document; import org.xml.sax.SAXException; @@ -243,10 +243,18 @@ public class LibvirtComputingResourceTest { @Mock Domain domainMock; + @Mock + DomainInfo domainInfoMock; + @Mock + DomainInterfaceStats domainInterfaceStatsMock; + @Mock + DomainBlockStats domainBlockStatsMock; private final static long HYPERVISOR_LIBVIRT_VERSION_SUPPORTS_IOURING = 6003000; private final static long HYPERVISOR_QEMU_VERSION_SUPPORTS_IOURING = 5000000; + private final static String VM_NAME = "test"; + String hyperVisorType = "kvm"; Random random = new Random(); final String memInfo = "MemTotal: 5830236 kB\n" + @@ -922,86 +930,6 @@ public void testUUID() { assertEquals(uuid, oldUuid); } - private static final String VMNAME = "test"; - - @Test - public void testGetVmStat() throws LibvirtException { - final Connect connect = Mockito.mock(Connect.class); - final Domain domain = Mockito.mock(Domain.class); - final DomainInfo domainInfo = new DomainInfo(); - final MemoryStatistic[] domainMem = new MemoryStatistic[2]; - domainMem[0] = Mockito.mock(MemoryStatistic.class); - Mockito.when(domain.getInfo()).thenReturn(domainInfo); - Mockito.when(domain.memoryStats(20)).thenReturn(domainMem); - Mockito.when(domainMem[0].getTag()).thenReturn(4); - Mockito.when(connect.domainLookupByName(VMNAME)).thenReturn(domain); - - // this is testing the interface stats, returns an increasing number of sent and received bytes - - Mockito.when(domain.interfaceStats(nullable(String.class))).thenAnswer(new org.mockito.stubbing.Answer() { - // increment with less than a KB, so this should be less than 1 KB - final static int increment = 1000; - int rxBytes = 1000; - int txBytes = 1000; - - @Override - public DomainInterfaceStats answer(final InvocationOnMock invocation) throws Throwable { - final DomainInterfaceStats domainInterfaceStats = new DomainInterfaceStats(); - domainInterfaceStats.rx_bytes = rxBytes += increment; - domainInterfaceStats.tx_bytes = txBytes += increment; - return domainInterfaceStats; - - } - - }); - - - Mockito.when(domain.blockStats(nullable(String.class))).thenAnswer(new org.mockito.stubbing.Answer() { - // a little less than a KB - final static int increment = 1000; - - int rdBytes = 0; - int wrBytes = 1024; - - @Override - public DomainBlockStats answer(final InvocationOnMock invocation) throws Throwable { - final DomainBlockStats domainBlockStats = new DomainBlockStats(); - - domainBlockStats.rd_bytes = rdBytes += increment; - domainBlockStats.wr_bytes = wrBytes += increment; - return domainBlockStats; - } - - }); - - final LibvirtComputingResource libvirtComputingResource = new LibvirtComputingResource() { - @Override - public List getInterfaces(final Connect conn, final String vmName) { - final InterfaceDef interfaceDef = new InterfaceDef(); - return Arrays.asList(interfaceDef); - } - - @Override - public List getDisks(final Connect conn, final String vmName) { - final DiskDef diskDef = new DiskDef(); - return Arrays.asList(diskDef); - } - - }; - libvirtComputingResource.getVmStat(connect, VMNAME); - final VmStatsEntry vmStat = libvirtComputingResource.getVmStat(connect, VMNAME); - // network traffic as generated by the logic above, must be greater than zero - Assert.assertTrue(vmStat.getNetworkReadKBs() > 0); - Assert.assertTrue(vmStat.getNetworkWriteKBs() > 0); - // IO traffic as generated by the logic above, must be greater than zero - Assert.assertTrue(vmStat.getDiskReadKBs() > 0); - Assert.assertTrue(vmStat.getDiskWriteKBs() > 0); - // Memory limit of VM must be greater than zero - Assert.assertTrue(vmStat.getIntFreeMemoryKBs() >= 0); - Assert.assertTrue(vmStat.getMemoryKBs() >= 0); - Assert.assertTrue(vmStat.getTargetMemoryKBs() >= vmStat.getMemoryKBs()); - } - /* * New Tests */ @@ -6331,4 +6259,160 @@ public void testGetHostTagsWithEmptyPropertyValue() throws ConfigurationExceptio Assert.assertEquals("", StringUtils.join(hostTagsList, ",")); } } + + @Test + public void getVmStatTestVmIsNullReturnsNull() throws LibvirtException { + doReturn(null).when(libvirtComputingResourceSpy).getDomain(connMock, VM_NAME); + + VmStatsEntry stat = libvirtComputingResourceSpy.getVmStat(connMock, VM_NAME); + + verify(libvirtComputingResourceSpy).getDomain(connMock, VM_NAME); + verify(libvirtComputingResourceSpy, never()).getVmCurrentStats(domainMock); + verify(libvirtComputingResourceSpy, never()).calculateVmMetrics(Mockito.any(), Mockito.any(), Mockito.any()); + Assert.assertNull(stat); + } + + @Test + public void getVmStatTestVmIsNotNullReturnsMetrics() throws LibvirtException { + doReturn(domainMock).when(libvirtComputingResourceSpy).getDomain(connMock, VM_NAME); + doReturn(Mockito.mock(LibvirtExtendedVmStatsEntry.class)).when(libvirtComputingResourceSpy).getVmCurrentStats(domainMock); + doReturn(Mockito.mock(VmStatsEntry.class)).when(libvirtComputingResourceSpy).calculateVmMetrics(Mockito.any(), Mockito.any(), Mockito.any()); + + VmStatsEntry stat = libvirtComputingResourceSpy.getVmStat(connMock, VM_NAME); + + verify(libvirtComputingResourceSpy).getDomain(connMock, VM_NAME); + verify(libvirtComputingResourceSpy).getVmCurrentStats(domainMock); + verify(libvirtComputingResourceSpy).calculateVmMetrics(Mockito.any(), Mockito.any(), Mockito.any()); + Assert.assertNotNull(stat); + } + + private void prepareVmInfoForGetVmCurrentStats() throws LibvirtException { + final NodeInfo nodeInfo = new NodeInfo(); + nodeInfo.cpus = 8; + nodeInfo.memory = 8 * 1024 * 1024; + nodeInfo.sockets = 2; + nodeInfo.threads = 2; + nodeInfo.model = "Foo processor"; + + Mockito.when(domainMock.getName()).thenReturn(VM_NAME); + Mockito.when(domainMock.getConnect()).thenReturn(connMock); + domainInfoMock.cpuTime = 500L; + domainInfoMock.nrVirtCpu = 4; + domainInfoMock.memory = 2048; + domainInfoMock.maxMem = 4096; + Mockito.when(domainMock.getInfo()).thenReturn(domainInfoMock); + final MemoryStatistic[] domainMem = new MemoryStatistic[2]; + domainMem[0] = Mockito.mock(MemoryStatistic.class); + doReturn(1024L).when(libvirtComputingResourceSpy).getMemoryFreeInKBs(domainMock); + + domainInterfaceStatsMock.rx_bytes = 1000L; + domainInterfaceStatsMock.tx_bytes = 2000L; + doReturn(domainInterfaceStatsMock).when(domainMock).interfaceStats(Mockito.any()); + doReturn(List.of(new InterfaceDef())).when(libvirtComputingResourceSpy).getInterfaces(connMock, VM_NAME); + + domainBlockStatsMock.rd_req = 3000L; + domainBlockStatsMock.rd_bytes = 4000L; + domainBlockStatsMock.wr_req = 5000L; + domainBlockStatsMock.wr_bytes = 6000L; + doReturn(domainBlockStatsMock).when(domainMock).blockStats(Mockito.any()); + doReturn(List.of(new DiskDef())).when(libvirtComputingResourceSpy).getDisks(connMock, VM_NAME); + } + + @Test + public void getVmCurrentStatsTestIfStatsAreAsExpected() throws LibvirtException { + prepareVmInfoForGetVmCurrentStats(); + + LibvirtExtendedVmStatsEntry vmStatsEntry = libvirtComputingResourceSpy.getVmCurrentStats(domainMock); + + Assert.assertEquals(domainInfoMock.cpuTime, vmStatsEntry.getCpuTime()); + Assert.assertEquals((double) domainInterfaceStatsMock.rx_bytes / 1024, vmStatsEntry.getNetworkReadKBs(), 0); + Assert.assertEquals((double) domainInterfaceStatsMock.tx_bytes / 1024, vmStatsEntry.getNetworkWriteKBs(), 0); + Assert.assertEquals(domainBlockStatsMock.rd_req, vmStatsEntry.getDiskReadIOs(), 0); + Assert.assertEquals((double) domainBlockStatsMock.rd_bytes / 1024, vmStatsEntry.getDiskReadKBs(), 0); + Assert.assertEquals(domainBlockStatsMock.wr_req, vmStatsEntry.getDiskWriteIOs(), 0); + Assert.assertEquals((double) domainBlockStatsMock.wr_bytes / 1024, vmStatsEntry.getDiskWriteKBs(), 0); + Assert.assertNotNull(vmStatsEntry.getTimestamp()); + } + + @Test + public void getVmCurrentCpuStatsTestIfStatsAreAsExpected() throws LibvirtException { + prepareVmInfoForGetVmCurrentStats(); + + LibvirtExtendedVmStatsEntry vmStatsEntry = new LibvirtExtendedVmStatsEntry(); + libvirtComputingResourceSpy.getVmCurrentCpuStats(domainMock, vmStatsEntry); + + Assert.assertEquals(domainInfoMock.cpuTime, vmStatsEntry.getCpuTime()); + } + + @Test + public void getVmCurrentNetworkStatsTestIfStatsAreAsExpected() throws LibvirtException { + prepareVmInfoForGetVmCurrentStats(); + + LibvirtExtendedVmStatsEntry vmStatsEntry = new LibvirtExtendedVmStatsEntry(); + libvirtComputingResourceSpy.getVmCurrentNetworkStats(domainMock, vmStatsEntry); + + Assert.assertEquals((double) domainInterfaceStatsMock.rx_bytes / 1024, vmStatsEntry.getNetworkReadKBs(), 0); + Assert.assertEquals((double) domainInterfaceStatsMock.tx_bytes / 1024, vmStatsEntry.getNetworkWriteKBs(), 0); + } + + @Test + public void getVmCurrentDiskStatsTestIfStatsAreAsExpected() throws LibvirtException { + prepareVmInfoForGetVmCurrentStats(); + + LibvirtExtendedVmStatsEntry vmStatsEntry = new LibvirtExtendedVmStatsEntry(); + libvirtComputingResourceSpy.getVmCurrentDiskStats(domainMock, vmStatsEntry); + + Assert.assertEquals(domainBlockStatsMock.rd_req, vmStatsEntry.getDiskReadIOs(), 0); + Assert.assertEquals((double) domainBlockStatsMock.rd_bytes / 1024, vmStatsEntry.getDiskReadKBs(), 0); + Assert.assertEquals(domainBlockStatsMock.wr_req, vmStatsEntry.getDiskWriteIOs(), 0); + Assert.assertEquals((double) domainBlockStatsMock.wr_bytes / 1024, vmStatsEntry.getDiskWriteKBs(), 0); + } + + @Test + public void calculateVmMetricsTestOldStatsIsNullDoesNotCalculateUtilization() throws LibvirtException { + prepareVmInfoForGetVmCurrentStats(); + + LibvirtExtendedVmStatsEntry vmStatsEntry = libvirtComputingResourceSpy.getVmCurrentStats(domainMock); + VmStatsEntry metrics = libvirtComputingResourceSpy.calculateVmMetrics(domainMock, null, vmStatsEntry); + + Assert.assertEquals(domainInfoMock.nrVirtCpu, metrics.getNumCPUs()); + Assert.assertEquals(domainInfoMock.maxMem, (long) metrics.getMemoryKBs()); + Assert.assertEquals(libvirtComputingResourceSpy.getMemoryFreeInKBs(domainMock), (long) metrics.getIntFreeMemoryKBs()); + Assert.assertEquals(domainInfoMock.memory, (long) metrics.getTargetMemoryKBs()); + Assert.assertEquals(0, metrics.getCPUUtilization(), 0); + Assert.assertEquals(0, metrics.getNetworkReadKBs(), 0); + Assert.assertEquals(0, metrics.getNetworkWriteKBs(), 0); + Assert.assertEquals(0, metrics.getDiskReadKBs(), 0); + Assert.assertEquals(0, metrics.getDiskReadIOs(), 0); + Assert.assertEquals(0, metrics.getDiskWriteKBs(), 0); + Assert.assertEquals(0, metrics.getDiskWriteIOs(), 0); + } + + @Test + public void calculateVmMetricsTestOldStatsIsNotNullCalculatesUtilization() throws LibvirtException { + prepareVmInfoForGetVmCurrentStats(); + LibvirtExtendedVmStatsEntry oldStats = libvirtComputingResourceSpy.getVmCurrentStats(domainMock); + domainInfoMock.cpuTime *= 3; + domainInterfaceStatsMock.rx_bytes *= 3; + domainInterfaceStatsMock.tx_bytes *= 3; + domainBlockStatsMock.rd_req *= 3; + domainBlockStatsMock.rd_bytes *= 3; + domainBlockStatsMock.wr_req *= 3; + domainBlockStatsMock.wr_bytes *= 3; + LibvirtExtendedVmStatsEntry newStats = libvirtComputingResourceSpy.getVmCurrentStats(domainMock); + + VmStatsEntry metrics = libvirtComputingResourceSpy.calculateVmMetrics(domainMock, oldStats, newStats); + + Assert.assertEquals(domainInfoMock.nrVirtCpu, metrics.getNumCPUs()); + Assert.assertEquals(domainInfoMock.maxMem, (long) metrics.getMemoryKBs()); + Assert.assertEquals(libvirtComputingResourceSpy.getMemoryFreeInKBs(domainMock), (long) metrics.getIntFreeMemoryKBs()); + Assert.assertEquals(domainInfoMock.memory, (long) metrics.getTargetMemoryKBs()); + Assert.assertTrue(metrics.getCPUUtilization() > 0); + Assert.assertEquals(newStats.getNetworkReadKBs() - oldStats.getNetworkReadKBs(), metrics.getNetworkReadKBs(), 0); + Assert.assertEquals(newStats.getNetworkWriteKBs() - oldStats.getNetworkWriteKBs(), metrics.getNetworkWriteKBs(), 0); + Assert.assertEquals(newStats.getDiskReadIOs() - oldStats.getDiskReadIOs(), metrics.getDiskReadIOs(), 0); + Assert.assertEquals(newStats.getDiskWriteIOs() - oldStats.getDiskWriteIOs(), metrics.getDiskWriteIOs(), 0); + Assert.assertEquals(newStats.getDiskReadKBs() - oldStats.getDiskReadKBs(), metrics.getDiskReadKBs(), 0); + Assert.assertEquals(newStats.getDiskWriteKBs() - oldStats.getDiskWriteKBs(), metrics.getDiskWriteKBs(), 0); + } } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java index 3d08522a40b8..53fbf0def58c 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java @@ -33,6 +33,7 @@ import com.google.common.collect.Lists; import com.cloud.agent.api.to.NfsTO; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.vmware.mo.DatastoreMO; import com.cloud.hypervisor.vmware.mo.HostMO; import com.cloud.hypervisor.vmware.util.VmwareClient; @@ -668,7 +669,7 @@ private Long getTemplateSize(VirtualMachineMO template, String vmInternalName, M private VMTemplateVO createVMTemplateRecord(String vmInternalName, long guestOsId, long accountId) { Long nextTemplateId = vmTemplateDao.getNextInSequence(Long.class, "id"); VMTemplateVO templateVO = new VMTemplateVO(nextTemplateId, "Imported-from-" + vmInternalName, Storage.ImageFormat.OVA, false, false, false, Storage.TemplateType.USER, null, - false, 64, accountId, null, "Template imported from VM " + vmInternalName, false, guestOsId, false, HypervisorType.VMware, null, null, false, false, false, false); + false, 64, accountId, null, "Template imported from VM " + vmInternalName, false, guestOsId, false, HypervisorType.VMware, null, null, false, false, false, false, CPU.CPUArch.amd64); return vmTemplateDao.persist(templateVO); } diff --git a/plugins/integrations/cloudian/src/main/java/org/apache/cloudstack/cloudian/client/CloudianUtils.java b/plugins/integrations/cloudian/src/main/java/org/apache/cloudstack/cloudian/client/CloudianUtils.java index 882d615ca0b2..6d70e785d0a4 100644 --- a/plugins/integrations/cloudian/src/main/java/org/apache/cloudstack/cloudian/client/CloudianUtils.java +++ b/plugins/integrations/cloudian/src/main/java/org/apache/cloudstack/cloudian/client/CloudianUtils.java @@ -81,12 +81,8 @@ public static String generateSSOUrl(final String cmcUrlPath, final String user, return null; } - stringBuilder.append("&redirect="); - if (group.equals("0")) { - stringBuilder.append("admin.htm"); - } else { - stringBuilder.append("explorer.htm"); - } + // Redirects to dashboard for admin users or the bucket browser for regular users + stringBuilder.append("&redirect=/"); return cmcUrlPath + "ssosecurelogin.htm?" + stringBuilder.toString(); } diff --git a/plugins/integrations/cloudian/src/test/java/org/apache/cloudstack/cloudian/CloudianUtilsTest.java b/plugins/integrations/cloudian/src/test/java/org/apache/cloudstack/cloudian/CloudianUtilsTest.java new file mode 100644 index 000000000000..4bc9ce1fe284 --- /dev/null +++ b/plugins/integrations/cloudian/src/test/java/org/apache/cloudstack/cloudian/CloudianUtilsTest.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.cloudian; + +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.HashMap; +import org.apache.cloudstack.cloudian.client.CloudianUtils; +import org.junit.Assert; +import org.junit.Test; + +public class CloudianUtilsTest { + + @Test + public void testGenerateSSOUrl() { + final String cmcUrlPath = "https://cmc.cloudian.com:8443/Cloudian/"; + final String user = "abc-def-ghi"; + final String group = "uvw-xyz"; + final String ssoKey = "randomkey"; + + // test expectations + final String expPath = "/Cloudian/ssosecurelogin.htm"; + HashMap expected = new HashMap(); + expected.put("user", user); + expected.put("group", group); + expected.put("timestamp", null); // null value will not be checked by this test + expected.put("signature", null); // null value will not be checked by this test + expected.put("redirect", "/"); + + // Generated URL will be something like this + // https://cmc.cloudian.com:8443/Cloudian/ssosecurelogin.htm?user=abc-def-ghi&group=uvw-xyz×tamp=1725937474949&signature=Wu1hjafeyE82mGwd1MIwrp5hPt4%3D&redirect=/ + String output = CloudianUtils.generateSSOUrl(cmcUrlPath, user, group, ssoKey); + Assert.assertNotNull(output); + + // Check main parts of the output URL + URL url = null; + try { + url = new URL(output); + } catch (MalformedURLException e) { + Assert.fail("failed to parse URL: " + output); + } + String path = url.getPath(); + Assert.assertEquals(expPath, path); + + // No easy way to check Query parameters in Java still? + // Just do a rudementary check as we are in charge of the URL + String query = url.getQuery(); + String[] nameValues = query.split("&"); + int matchedCount = 0; + for(String nameValue : nameValues) { + String[] nameValuePair = nameValue.split("=", 2); + Assert.assertEquals(nameValue, 2, nameValuePair.length); + String name = null; + String value = null; + try { + name = URLDecoder.decode(nameValuePair[0], "UTF-8"); + value = URLDecoder.decode(nameValuePair[1], "UTF-8"); + } catch (UnsupportedEncodingException e) { + Assert.fail("not expecting UTF-8 to fail"); + } + Assert.assertTrue(expected.containsKey(name)); + matchedCount++; + String expValue = expected.get(name); + if (expValue != null) { + Assert.assertEquals("Parameter " + name, expValue, value); + } + } + Assert.assertEquals("Should be 5 query parameters", 5, matchedCount); + } +} 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 06f92181506b..39e4bbf935fc 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 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.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.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.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 @@ private void validateNetwork(Network network, int clusterTotalNodeCount) { 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 @@ private void validateNetwork(Network network, int clusterTotalNodeCount) { 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 KubernetesClusterResponse createKubernetesClusterResponse(long kubernetes 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 @@ private void validateManagedKubernetesClusterCreateParameters(final CreateKubern 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 @@ private Network getKubernetesClusterNetworkIfMissing(final String clusterName, f } 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 void doInTransactionWithoutResult(TransactionStatus status) { 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 @@ private void createNetworkOfferingForKubernetes(String offeringName, String offe 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 @@ private void createNetworkOfferingForKubernetes(String offeringName, String offe } } + @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 6acc876493ef..9d86c564de48 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.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 0ef3ee96d328..743962a1f00b 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.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; @@ -361,7 +360,7 @@ protected IpAddress getVpcTierKubernetesPublicIp(Network network) { return null; } IpAddress address = ipAddressDao.findByUuid(detailsVO.getValue()); - if (address == null || network.getVpcId() != address.getVpcId()) { + if (address == null || !Objects.equals(network.getVpcId(), address.getVpcId())) { logger.warn(String.format("Public IP with ID: %s linked to the Kubernetes cluster: %s is not usable", detailsVO.getValue(), kubernetesCluster.getName())); return null; } @@ -429,13 +428,14 @@ protected Pair getKubernetesClusterServerIpSshPort(UserVm contr 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 @@ protected boolean taintControlNodes() { 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 047d32f1f9fa..50d7fb14085a 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 @@ private void validateClusterVMsDestroyed() { 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/KubernetesClusterResourceModifierActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java index 92a0315efb56..ec461610e328 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java @@ -17,6 +17,31 @@ package com.cloud.kubernetes.cluster.actionworkers; +import static com.cloud.utils.NumbersUtil.toHumanReadableSize; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +import com.cloud.network.rules.FirewallManager; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.dao.NetworkOfferingDao; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.command.user.firewall.CreateFirewallRuleCmd; +import org.apache.cloudstack.api.command.user.network.CreateNetworkACLCmd; +import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + import com.cloud.capacity.CapacityManager; import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterDetailsVO; @@ -61,9 +86,7 @@ import com.cloud.network.vpc.NetworkACLItemDao; import com.cloud.network.vpc.NetworkACLItemVO; import com.cloud.network.vpc.NetworkACLService; -import com.cloud.offering.NetworkOffering; import com.cloud.offering.ServiceOffering; -import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.resource.ResourceManager; import com.cloud.storage.Volume; import com.cloud.storage.VolumeApiService; @@ -88,29 +111,9 @@ import com.cloud.vm.VmDetailConstants; import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.api.ApiCommandResourceType; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.command.user.firewall.CreateFirewallRuleCmd; -import org.apache.cloudstack.api.command.user.network.CreateNetworkACLCmd; -import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; import org.apache.cloudstack.context.CallContext; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Level; -import javax.inject.Inject; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -import static com.cloud.utils.NumbersUtil.toHumanReadableSize; - public class KubernetesClusterResourceModifierActionWorker extends KubernetesClusterActionWorker { @Inject @@ -134,6 +137,8 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu @Inject protected RulesService rulesService; @Inject + protected FirewallManager firewallManager; + @Inject protected PortForwardingRulesDao portForwardingRulesDao; @Inject protected ResourceManager resourceManager; @@ -169,6 +174,7 @@ private String getKubernetesNodeConfig(final String joinIp, final boolean ejectI final String joinIpKey = "{{ k8s_control_node.join_ip }}"; final String clusterTokenKey = "{{ k8s_control_node.cluster.token }}"; final String ejectIsoKey = "{{ k8s.eject.iso }}"; + String pubKey = "- \"" + configurationDao.getValue("ssh.publickey") + "\""; String sshKeyPair = kubernetesCluster.getKeyPair(); if (StringUtils.isNotEmpty(sshKeyPair)) { @@ -181,7 +187,6 @@ private String getKubernetesNodeConfig(final String joinIp, final boolean ejectI k8sNodeConfig = k8sNodeConfig.replace(joinIpKey, joinIp); k8sNodeConfig = k8sNodeConfig.replace(clusterTokenKey, KubernetesClusterUtil.generateClusterToken(kubernetesCluster)); k8sNodeConfig = k8sNodeConfig.replace(ejectIsoKey, String.valueOf(ejectIso)); - k8sNodeConfig = updateKubeConfigWithRegistryDetails(k8sNodeConfig); return k8sNodeConfig; @@ -522,17 +527,22 @@ protected FirewallRule removeSshFirewallRule(final IpAddress publicIp) { protected void removePortForwardingRules(final IpAddress publicIp, final Network network, final Account account, final List removedVMIds) throws ResourceUnavailableException { if (!CollectionUtils.isEmpty(removedVMIds)) { + List pfRules = new ArrayList<>(); + List revokedRules = new ArrayList<>(); for (Long vmId : removedVMIds) { - List pfRules = portForwardingRulesDao.listByNetwork(network.getId()); + pfRules.addAll(portForwardingRulesDao.listByNetwork(network.getId())); for (PortForwardingRuleVO pfRule : pfRules) { if (pfRule.getVirtualMachineId() == vmId) { portForwardingRulesDao.remove(pfRule.getId()); + logger.trace("Marking PF rule {} with Revoke state", pfRule); + pfRule.setState(FirewallRule.State.Revoke); + revokedRules.add(pfRule); logger.debug("The Port forwarding rule [%s] with the id [%s] was removed.", pfRule.getName(), pfRule.getId()); break; } } } - rulesService.applyPortForwardingRules(publicIp.getId(), account); + firewallManager.applyRules(revokedRules, false, true); } } @@ -542,10 +552,11 @@ protected void removePortForwardingRules(final IpAddress publicIp, final Network for (PortForwardingRuleVO pfRule : pfRules) { if (startPort <= pfRule.getSourcePortStart() && pfRule.getSourcePortStart() <= endPort) { portForwardingRulesDao.remove(pfRule.getId()); - logger.debug("The Port forwarding rule [{}] with the id [{}] was removed.", pfRule.getName(), pfRule.getId()); + logger.debug("The Port forwarding rule [{}] with the id [{}] was mark as revoked.", pfRule.getName(), pfRule.getId()); + pfRule.setState(FirewallRule.State.Revoke); } } - rulesService.applyPortForwardingRules(publicIp.getId(), account); + firewallManager.applyRules(pfRules, false, true); } protected void removeLoadBalancingRule(final IpAddress publicIp, final Network network, 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 f5ac553733d6..aa500de91902 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 @@ private void scaleKubernetesClusterVpcTierRules(final List clusterVMIds) t * @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 9e8a43791e8e..c3b81a6ee1fe 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 @@ -139,6 +139,7 @@ private String getKubernetesControlNodeConfig(final String controlNodeIp, final final String clusterToken = "{{ k8s_control_node.cluster.token }}"; final String clusterInitArgsKey = "{{ k8s_control_node.cluster.initargs }}"; final String ejectIsoKey = "{{ k8s.eject.iso }}"; + final List addresses = new ArrayList<>(); addresses.add(controlNodeIp); if (!serverIp.equals(controlNodeIp)) { @@ -189,7 +190,7 @@ private UserVm createKubernetesControlNode(final Network network, String serverI 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); @@ -243,6 +244,7 @@ private String getKubernetesAdditionalControlNodeConfig(final String joinIp, fin final String sshPubKey = "{{ k8s.ssh.pub.key }}"; final String clusterHACertificateKey = "{{ k8s_control_node.cluster.ha.certificate.key }}"; final String ejectIsoKey = "{{ k8s.eject.iso }}"; + String pubKey = "- \"" + configurationDao.getValue("ssh.publickey") + "\""; String sshKeyPair = kubernetesCluster.getKeyPair(); if (StringUtils.isNotEmpty(sshKeyPair)) { @@ -380,9 +382,9 @@ private Network startKubernetesClusterNetwork(final DeployDestination destinatio } 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 +494,7 @@ public boolean startKubernetesClusterOnCreate() { } 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 +539,7 @@ public boolean startKubernetesClusterOnCreate() { 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/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java index ba2b4288d480..93e1ae2810a6 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/version/KubernetesVersionManagerImpl.java @@ -22,6 +22,7 @@ import javax.inject.Inject; +import com.cloud.cpu.CPU; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.admin.kubernetes.version.AddKubernetesSupportedVersionCmd; @@ -159,7 +160,7 @@ private List filterKubernetesSupportedVersions(Li return versions; } - private VirtualMachineTemplate registerKubernetesVersionIso(final Long zoneId, final String versionName, final String isoUrl, final String isoChecksum, final boolean directDownload) throws IllegalAccessException, NoSuchFieldException, + private VirtualMachineTemplate registerKubernetesVersionIso(final Long zoneId, final String versionName, final String isoUrl, final String isoChecksum, final boolean directDownload, CPU.CPUArch arch) throws IllegalAccessException, NoSuchFieldException, IllegalArgumentException, ResourceAllocationException { CallContext.register(CallContext.current(), ApiCommandResourceType.Iso); String isoName = String.format("%s-Kubernetes-Binaries-ISO", versionName); @@ -177,6 +178,7 @@ private VirtualMachineTemplate registerKubernetesVersionIso(final Long zoneId, f registerIsoCmd.setChecksum(isoChecksum); } registerIsoCmd.setDirectDownload(directDownload); + registerIsoCmd.setArch(arch.getType()); registerIsoCmd.setAccountName(accountManager.getSystemAccount().getAccountName()); registerIsoCmd.setDomainId(accountManager.getSystemAccount().getDomainId()); try { @@ -316,6 +318,8 @@ public KubernetesSupportedVersionResponse addKubernetesSupportedVersion(final Ad final Integer minimumCpu = cmd.getMinimumCpu(); final Integer minimumRamSize = cmd.getMinimumRamSize(); final boolean isDirectDownload = cmd.isDirectDownload(); + CPU.CPUArch arch = cmd.getArch(); + if (minimumCpu == null || minimumCpu < KubernetesClusterService.MIN_KUBERNETES_CLUSTER_NODE_CPU) { throw new InvalidParameterValueException(String.format("Invalid value for %s parameter. Minimum %d vCPUs required.", ApiConstants.MIN_CPU_NUMBER, KubernetesClusterService.MIN_KUBERNETES_CLUSTER_NODE_CPU)); } @@ -346,7 +350,7 @@ public KubernetesSupportedVersionResponse addKubernetesSupportedVersion(final Ad VMTemplateVO template = null; try { - VirtualMachineTemplate vmTemplate = registerKubernetesVersionIso(zoneId, name, isoUrl, isoChecksum, isDirectDownload); + VirtualMachineTemplate vmTemplate = registerKubernetesVersionIso(zoneId, name, isoUrl, isoChecksum, isDirectDownload, arch); template = templateDao.findById(vmTemplate.getId()); } catch (IllegalAccessException | NoSuchFieldException | IllegalArgumentException | ResourceAllocationException ex) { logger.error(String.format("Unable to register binaries ISO for supported kubernetes version, %s, with url: %s", name, isoUrl), ex); diff --git a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/admin/kubernetes/version/AddKubernetesSupportedVersionCmd.java b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/admin/kubernetes/version/AddKubernetesSupportedVersionCmd.java index 139b79a182a1..592ca6b0c259 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/admin/kubernetes/version/AddKubernetesSupportedVersionCmd.java +++ b/plugins/integrations/kubernetes-service/src/main/java/org/apache/cloudstack/api/command/admin/kubernetes/version/AddKubernetesSupportedVersionCmd.java @@ -19,6 +19,7 @@ import javax.inject.Inject; +import com.cloud.cpu.CPU; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -87,6 +88,11 @@ public class AddKubernetesSupportedVersionCmd extends BaseCmd implements AdminCm description = "If set to true the Kubernetes supported version ISO will bypass Secondary Storage and be downloaded to Primary Storage on deployment. Default is false") private Boolean directDownload; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the Kubernetes ISO. Valid options are: x86_64, aarch64", + since = "4.20") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -130,6 +136,10 @@ public boolean isDirectDownload() { return (directDownload != null) ? directDownload : false; } + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + @Override public long getEntityOwnerId() { return CallContext.current().getCallingAccountId(); diff --git a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionServiceTest.java b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionServiceTest.java index f7e816596c26..e6d25df10efa 100644 --- a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionServiceTest.java +++ b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/version/KubernetesVersionServiceTest.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.UUID; +import com.cloud.cpu.CPU; import org.apache.cloudstack.api.command.admin.kubernetes.version.AddKubernetesSupportedVersionCmd; import org.apache.cloudstack.api.command.admin.kubernetes.version.DeleteKubernetesSupportedVersionCmd; import org.apache.cloudstack.api.command.admin.kubernetes.version.UpdateKubernetesSupportedVersionCmd; @@ -213,6 +214,7 @@ public void addKubernetesSupportedVersionIsoUrlTest() throws ResourceAllocationE when(cmd.getChecksum()).thenReturn(null); when(cmd.getMinimumCpu()).thenReturn(KubernetesClusterService.MIN_KUBERNETES_CLUSTER_NODE_CPU); when(cmd.getMinimumRamSize()).thenReturn(KubernetesClusterService.MIN_KUBERNETES_CLUSTER_NODE_RAM_SIZE); + when(cmd.getArch()).thenReturn(CPU.CPUArch.amd64); Account systemAccount = new AccountVO("system", 1L, "", Account.Type.ADMIN, "uuid"); when(accountManager.getSystemAccount()).thenReturn(systemAccount); CallContext callContext = Mockito.mock(CallContext.class); 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 791e245b17ce..6da2e216ff82 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 @@ private NetworkOffering locatePublicNetworkOffering(String offeringName, 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 @@ private NetworkOffering locateNetworkOffering(String offeringName, 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 @@ private VpcOffering locateVpcOffering() { } 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/NsxAnswer.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/NsxAnswer.java index 0820465a6b68..a667adda7945 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/NsxAnswer.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/NsxAnswer.java @@ -20,6 +20,9 @@ import com.cloud.agent.api.Command; public class NsxAnswer extends Answer { + + private boolean objectExists; + public NsxAnswer(final Command command, final boolean success, final String details) { super(command, success, details); } @@ -28,4 +31,11 @@ public NsxAnswer(final Command command, final Exception e) { super(command, e); } + public boolean isObjectExistent() { + return objectExists; + } + + public void setObjectExists(boolean objectExisted) { + this.objectExists = objectExisted; + } } diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java index cd1d481b9f8d..76815b0deebe 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java @@ -18,6 +18,8 @@ import com.cloud.agent.IAgentControl; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CheckHealthAnswer; +import com.cloud.agent.api.CheckHealthCommand; import com.cloud.agent.api.Command; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.ReadyAnswer; @@ -102,6 +104,8 @@ public PingCommand getCurrentStatus(long id) { public Answer executeRequest(Command cmd) { if (cmd instanceof ReadyCommand) { return executeRequest((ReadyCommand) cmd); + } else if (cmd instanceof CheckHealthCommand) { + return executeRequest((CheckHealthCommand) cmd); } else if (cmd instanceof DeleteNsxTier1GatewayCommand) { return executeRequest((DeleteNsxTier1GatewayCommand) cmd); } else if (cmd instanceof DeleteNsxSegmentCommand) { @@ -293,6 +297,10 @@ private Answer executeRequest(ReadyCommand cmd) { return new ReadyAnswer(cmd); } + private Answer executeRequest(CheckHealthCommand cmd) { + return new CheckHealthAnswer(cmd, nsxApiClient.isNsxControllerActive()); + } + private Answer executeRequest(CreateNsxTier1GatewayCommand cmd) { String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), cmd.getNetworkResourceId(), cmd.isResourceVpc()); boolean sourceNatEnabled = cmd.isSourceNatEnabled(); @@ -385,16 +393,21 @@ private NsxAnswer executeRequest(CreateNsxPortForwardRuleCommand cmd) { cmd.getNetworkResourceId(), cmd.isResourceVpc()); try { String privatePort = cmd.getPrivatePort(); - String service = privatePort.contains("-") ? nsxApiClient.getServicePath(ruleName, privatePort, cmd.getProtocol(), null, null) : - nsxApiClient.getNsxInfraServices(ruleName, privatePort, cmd.getProtocol(), null, null); + logger.debug("Checking if the rule {} exists on Tier 1 Gateway: {}", ruleName, tier1GatewayName); if (nsxApiClient.doesPfRuleExist(ruleName, tier1GatewayName)) { - logger.debug(String.format("Port forward rule for port: %s exits on NSX, not adding it again", privatePort)); - return new NsxAnswer(cmd, true, null); + String msg = String.format("Port forward rule for port: %s (%s) exits on NSX, not adding it again", ruleName, privatePort); + logger.debug(msg); + NsxAnswer answer = new NsxAnswer(cmd, true, msg); + answer.setObjectExists(true); + return answer; } + String service = privatePort.contains("-") ? nsxApiClient.getServicePath(ruleName, privatePort, cmd.getProtocol(), null, null) : + nsxApiClient.getNsxInfraServices(ruleName, privatePort, cmd.getProtocol(), null, null); nsxApiClient.createPortForwardingRule(ruleName, tier1GatewayName, cmd.getNetworkResourceName(), cmd.getPublicIp(), cmd.getVmIp(), cmd.getPublicPort(), service); } catch (Exception e) { - logger.error(String.format("Failed to add NSX port forward rule %s for network: %s", ruleName, cmd.getNetworkResourceName())); + String msg = String.format("Failed to add NSX port forward rule %s for network: %s", ruleName, cmd.getNetworkResourceName()); + logger.error(msg, e); return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); } return new NsxAnswer(cmd, true, null); @@ -415,8 +428,9 @@ private NsxAnswer executeRequest(DeleteNsxNatRuleCommand cmd) { nsxApiClient.deleteNatRule(cmd.getService(), cmd.getPrivatePort(), cmd.getProtocol(), cmd.getNetworkResourceName(), tier1GatewayName, ruleName); } catch (Exception e) { - logger.error(String.format("Failed to add NSX static NAT rule %s for network: %s", ruleName, cmd.getNetworkResourceName())); - return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); + String msg = String.format("Failed to delete NSX rule %s for network %s: due to %s", ruleName, cmd.getNetworkResourceName(), e.getMessage()); + logger.error(msg, e); + return new NsxAnswer(cmd, new CloudRuntimeException(msg)); } return new NsxAnswer(cmd, true, null); } diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java index d443b0e14e79..1ba1cc0fcc3b 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java @@ -17,7 +17,11 @@ package org.apache.cloudstack.service; import com.cloud.network.Network; +import com.cloud.network.nsx.NsxService; import com.cloud.utils.exception.CloudRuntimeException; +import com.vmware.nsx.cluster.Status; +import com.vmware.nsx.model.ClusterStatus; +import com.vmware.nsx.model.ControllerClusterStatus; import com.vmware.nsx.model.TransportZone; import com.vmware.nsx.model.TransportZoneListResult; import com.vmware.nsx_policy.infra.DhcpRelayConfigs; @@ -45,13 +49,13 @@ import com.vmware.nsx_policy.model.ICMPTypeServiceEntry; import com.vmware.nsx_policy.model.L4PortSetServiceEntry; import com.vmware.nsx_policy.model.LBAppProfileListResult; +import com.vmware.nsx_policy.model.LBIcmpMonitorProfile; import com.vmware.nsx_policy.model.LBMonitorProfileListResult; import com.vmware.nsx_policy.model.LBPool; import com.vmware.nsx_policy.model.LBPoolListResult; import com.vmware.nsx_policy.model.LBPoolMember; import com.vmware.nsx_policy.model.LBService; import com.vmware.nsx_policy.model.LBTcpMonitorProfile; -import com.vmware.nsx_policy.model.LBUdpMonitorProfile; import com.vmware.nsx_policy.model.LBVirtualServer; import com.vmware.nsx_policy.model.LBVirtualServerListResult; import com.vmware.nsx_policy.model.LocaleServicesListResult; @@ -84,6 +88,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.commons.lang3.BooleanUtils; import java.util.ArrayList; import java.util.List; @@ -112,6 +117,7 @@ public class NsxApiClient { protected Logger logger = LogManager.getLogger(getClass()); // Constants + private static final String CLUSTER_STATUS_STABLE = "STABLE"; private static final String TIER_1_RESOURCE_TYPE = "Tier1"; private static final String TIER_1_LOCALE_SERVICE_ID = "default"; private static final String SEGMENT_RESOURCE_TYPE = "Segment"; @@ -123,7 +129,7 @@ public class NsxApiClient { // TODO: Pass as global / zone-level setting? protected static final String NSX_LB_PASSIVE_MONITOR = "/infra/lb-monitor-profiles/default-passive-lb-monitor"; protected static final String TCP_MONITOR_PROFILE = "LBTcpMonitorProfile"; - protected static final String UDP_MONITOR_PROFILE = "LBUdpMonitorProfile"; + protected static final String ICMP_MONITOR_PROFILE = "LBIcmpMonitorProfile"; protected static final String NAT_ID = "USER"; private enum PoolAllocation { ROUTING, LB_SMALL, LB_MEDIUM, LB_LARGE, LB_XLARGE } @@ -199,6 +205,26 @@ public NsxApiClient(String hostname, String port, String username, char[] passwo nsxService = apiClient::createStub; } + public boolean isNsxControllerActive() { + try { + Status statusService = (Status) nsxService.apply(Status.class); + ClusterStatus clusterStatus = statusService.get(); + if (clusterStatus == null) { + logger.error("Cannot get NSX Cluster Status"); + return false; + } + ControllerClusterStatus status = clusterStatus.getControlClusterStatus(); + if (status == null) { + logger.error("Cannot get NSX Controller Cluster Status"); + return false; + } + return CLUSTER_STATUS_STABLE.equalsIgnoreCase(status.getStatus()); + } catch (Error error) { + logger.error("Error checking NSX Controller Health: {}", error.getMessage()); + return false; + } + } + public void createTier1NatRule(String tier1GatewayName, String natId, String natRuleId, String action, String translatedIp) { NatRules natRulesService = (NatRules) nsxService.apply(NatRules.class); @@ -435,7 +461,7 @@ public void deleteSegment(long zoneId, long domainId, long accountId, Long vpcId String t1GatewayName = getTier1GatewayName(domainId, accountId, zoneId, networkId, false); deleteLoadBalancer(getLoadBalancerName(t1GatewayName)); } - removeSegment(segmentName); + removeSegment(segmentName, zoneId); DhcpRelayConfigs dhcpRelayConfig = (DhcpRelayConfigs) nsxService.apply(DhcpRelayConfigs.class); String dhcpRelayConfigId = NsxControllerUtils.getNsxDhcpRelayConfigId(zoneId, domainId, accountId, vpcId, networkId); logger.debug(String.format("Removing the DHCP relay config with ID %s", dhcpRelayConfigId)); @@ -448,7 +474,8 @@ public void deleteSegment(long zoneId, long domainId, long accountId, Long vpcId } } - protected void removeSegment(String segmentName) { + + protected void removeSegment(String segmentName, long zoneId) { logger.debug(String.format("Removing the segment with ID %s", segmentName)); Segments segmentService = (Segments) nsxService.apply(Segments.class); String errMsg = String.format("The segment with ID %s is not found, skipping removal", segmentName); @@ -467,15 +494,16 @@ protected void removeSegment(String segmentName) { SegmentPorts segmentPortsService = (SegmentPorts) nsxService.apply(SegmentPorts.class); PolicyGroupMembersListResult segmentPortsList = getSegmentPortList(segmentPortsService, segmentName, enforcementPointPath); Long portCount = segmentPortsList.getResultCount(); - portCount = retrySegmentDeletion(segmentPortsService, portCount, segmentName, enforcementPointPath); - logger.info("Port count: " + portCount); + if (portCount > 0L) { + portCount = retrySegmentDeletion(segmentPortsService, segmentName, enforcementPointPath, zoneId); + } if (portCount == 0L) { logger.debug(String.format("Removing the segment with ID %s", segmentName)); removeGroupForSegment(segmentName); segmentService.delete(segmentName); } else { String msg = String.format("Cannot remove the NSX segment %s because there are still %s port group(s) attached to it", segmentName, portCount); - logger.debug(msg); + logger.error(msg); throw new CloudRuntimeException(msg); } } @@ -485,13 +513,16 @@ private PolicyGroupMembersListResult getSegmentPortList(SegmentPorts segmentPort false, null, 50L, false, null); } - private Long retrySegmentDeletion(SegmentPorts segmentPortsService, Long portCount, String segmentName, String enforcementPointPath) { - int retries = 20; + private Long retrySegmentDeletion(SegmentPorts segmentPortsService, String segmentName, String enforcementPointPath, long zoneId) { + int retries = NsxService.NSX_API_FAILURE_RETRIES.valueIn(zoneId); + int waitingSecs = NsxService.NSX_API_FAILURE_INTERVAL.valueIn(zoneId); int count = 1; + Long portCount; do { try { - logger.info("Waiting for all port groups to be unlinked from the segment - Attempt: " + count++ + " Waiting for 5 secs"); - Thread.sleep(5000); + logger.info("Waiting for all port groups to be unlinked from the segment {} - " + + "Attempt: {}. Waiting for {} secs", segmentName, count++, waitingSecs); + Thread.sleep(waitingSecs * 1000L); portCount = getSegmentPortList(segmentPortsService, segmentName, enforcementPointPath).getResultCount(); retries--; } catch (InterruptedException e) { @@ -526,24 +557,37 @@ public void createStaticNatRule(String vpcName, String tier1GatewayName, } } + protected void deletePortForwardingNatRuleService(String ruleName, String privatePort, String protocol) { + String svcName = getServiceName(ruleName, privatePort, protocol, null, null); + try { + Services services = (Services) nsxService.apply(Services.class); + com.vmware.nsx_policy.model.Service servicePFRule = services.get(svcName); + if (servicePFRule != null && !servicePFRule.getMarkedForDelete() && !BooleanUtils.toBoolean(servicePFRule.getIsDefault())) { + services.delete(svcName); + } + } catch (Error error) { + String msg = String.format("Cannot find service %s associated to rule %s, skipping its deletion: %s", + svcName, ruleName, error.getMessage()); + logger.debug(msg); + } + } + public void deleteNatRule(Network.Service service, String privatePort, String protocol, String networkName, String tier1GatewayName, String ruleName) { try { NatRules natService = (NatRules) nsxService.apply(NatRules.class); - logger.debug(String.format("Deleting NSX static NAT rule %s for tier-1 gateway %s (network: %s)", ruleName, tier1GatewayName, networkName)); - // delete NAT rule - natService.delete(tier1GatewayName, NatId.USER.name(), ruleName); - if (service == Network.Service.PortForwarding) { - String svcName = getServiceName(ruleName, privatePort, protocol, null, null); - // Delete service - Services services = (Services) nsxService.apply(Services.class); - services.delete(svcName); + logger.debug("Deleting NSX NAT rule {} for tier-1 gateway {} (network: {})", ruleName, tier1GatewayName, networkName); + PolicyNatRule natRule = natService.get(tier1GatewayName, NatId.USER.name(), ruleName); + if (natRule != null && !natRule.getMarkedForDelete()) { + logger.debug("Deleting rule {} from Tier 1 Gateway {}", ruleName, tier1GatewayName); + natService.delete(tier1GatewayName, NatId.USER.name(), ruleName); } } catch (Error error) { - ApiError ae = error.getData()._convertTo(ApiError.class); - String msg = String.format("Failed to delete NSX Static NAT rule %s for tier-1 gateway %s (VPC: %s), due to %s", - ruleName, tier1GatewayName, networkName, ae.getErrorMessage()); - logger.error(msg); - throw new CloudRuntimeException(msg); + String msg = String.format("Cannot find NAT rule with name %s: %s, skipping deletion", ruleName, error.getMessage()); + logger.debug(msg); + } + + if (service == Network.Service.PortForwarding) { + deletePortForwardingNatRuleService(ruleName, privatePort, protocol); } } @@ -577,9 +621,14 @@ public boolean doesPfRuleExist(String ruleName, String tier1GatewayName) { try { NatRules natService = (NatRules) nsxService.apply(NatRules.class); PolicyNatRule rule = natService.get(tier1GatewayName, NAT_ID, ruleName); + logger.debug("Rule {} from Tier 1 GW {}: {}", ruleName, tier1GatewayName, + rule == null ? "null" : rule.getId() + " " + rule.getPath()); return !Objects.isNull(rule); } catch (Error error) { - logger.debug(String.format("Found a port forward rule named: %s on NSX", ruleName)); + String msg = String.format("Error checking if port forwarding rule %s exists on Tier 1 Gateway %s: %s", + ruleName, tier1GatewayName, error.getMessage()); + Throwable throwable = error.getCause(); + logger.error(msg, throwable); return false; } } @@ -637,13 +686,10 @@ private String getLbActiveMonitorPath(String lbServerPoolName, String port, Stri .build(); lbActiveMonitor.patch(lbMonitorProfileId, lbTcpMonitorProfile); } else if ("UDP".equals(protocol.toUpperCase(Locale.ROOT))) { - LBUdpMonitorProfile lbUdpMonitorProfile = new LBUdpMonitorProfile.Builder(UDP_MONITOR_PROFILE) + LBIcmpMonitorProfile icmpMonitorProfile = new LBIcmpMonitorProfile.Builder(ICMP_MONITOR_PROFILE) .setDisplayName(lbMonitorProfileId) - .setMonitorPort(Long.parseLong(port)) - .setSend("") - .setReceive("") .build(); - lbActiveMonitor.patch(lbMonitorProfileId, lbUdpMonitorProfile); + lbActiveMonitor.patch(lbMonitorProfileId, icmpMonitorProfile); } LBMonitorProfileListResult listResult = listLBActiveMonitors(lbActiveMonitor); diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java index d09049700e50..7673e5a60386 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java @@ -91,6 +91,8 @@ import com.cloud.utils.component.AdapterBase; import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.NicProfile; import com.cloud.vm.ReservationContext; @@ -98,7 +100,9 @@ import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.VMInstanceDao; import net.sf.ehcache.config.InvalidConfigurationException; +import org.apache.cloudstack.NsxAnswer; import org.apache.cloudstack.StartupNsxCommand; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.admin.internallb.ConfigureInternalLoadBalancerElementCmd; import org.apache.cloudstack.api.command.admin.internallb.CreateInternalLoadBalancerElementCmd; import org.apache.cloudstack.api.command.admin.internallb.ListInternalLoadBalancerElementsCmd; @@ -108,6 +112,8 @@ import org.apache.cloudstack.resource.NsxOpObject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.cloudstack.resourcedetail.FirewallRuleDetailVO; +import org.apache.cloudstack.resourcedetail.dao.FirewallRuleDetailsDao; import org.springframework.stereotype.Component; import javax.inject.Inject; @@ -121,6 +127,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.LongFunction; +import java.util.stream.Collectors; @Component public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsServiceProvider, VpcProvider, @@ -160,6 +167,8 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, Dns VirtualRouterProviderDao vrProviderDao; @Inject PhysicalNetworkServiceProviderDao pNtwkSvcProviderDao; + @Inject + FirewallRuleDetailsDao firewallRuleDetailsDao; protected Logger logger = LogManager.getLogger(getClass()); @@ -395,10 +404,18 @@ private Pair validateVpcConfigurationAndGetAccount(DataCenterV Account account = null; boolean forNsx = false; List physicalNetworks = physicalNetworkDao.listByZoneAndTrafficType(zone.getId(), Networks.TrafficType.Guest); - if (CollectionUtils.isNullOrEmpty(physicalNetworks) || physicalNetworks.size() > 1 ) { - throw new InvalidConfigurationException(String.format("Desired number of physical networks is not present in the zone %s for traffic type %s. ", zone.getName(), Networks.TrafficType.Guest.name())); - } - if (physicalNetworks.get(0).getIsolationMethods().contains("NSX")) { + if (CollectionUtils.isNullOrEmpty(physicalNetworks)) { + String err = String.format("Desired physical network is not present in the zone %s for traffic type %s. ", zone.getName(), Networks.TrafficType.Guest.name()); + logger.error(err); + throw new InvalidConfigurationException(err); + } + List filteredPhysicalNetworks = physicalNetworks.stream().filter(x -> x.getIsolationMethods().contains("NSX")).collect(Collectors.toList()); + if (CollectionUtils.isNullOrEmpty(filteredPhysicalNetworks)) { + String err = String.format("No physical network with NSX isolation type for traffic type %s is present in the zone %s.", Networks.TrafficType.Guest.name(), zone.getName()); + logger.error(err); + throw new InvalidConfigurationException(err); + } + if (filteredPhysicalNetworks.get(0).getIsolationMethods().contains("NSX")) { account = accountMgr.getAccount(vpc.getAccountId()); forNsx = true; } @@ -527,45 +544,77 @@ public boolean applyStaticNats(Network config, List rules) return false; } + protected synchronized boolean applyPFRulesInternal(Network network, List rules) { + return Transaction.execute((TransactionCallback) status -> { + boolean result = true; + for (PortForwardingRule rule : rules) { + IPAddressVO publicIp = ApiDBUtils.findIpAddressById(rule.getSourceIpAddressId()); + UserVm vm = ApiDBUtils.findUserVmById(rule.getVirtualMachineId()); + if (vm == null && rule.getState() != FirewallRule.State.Revoke) { + continue; + } + NsxOpObject nsxObject = getNsxOpObject(network); + String publicPort = getPublicPortRange(rule); + + String privatePort = getPrivatePFPortRange(rule); + + NsxNetworkRule networkRule = new NsxNetworkRule.Builder() + .setDomainId(nsxObject.getDomainId()) + .setAccountId(nsxObject.getAccountId()) + .setZoneId(nsxObject.getZoneId()) + .setNetworkResourceId(nsxObject.getNetworkResourceId()) + .setNetworkResourceName(nsxObject.getNetworkResourceName()) + .setVpcResource(nsxObject.isVpcResource()) + .setVmId(Objects.nonNull(vm) ? vm.getId() : 0) + .setVmIp(Objects.nonNull(vm) ? vm.getPrivateIpAddress() : null) + .setPublicIp(publicIp.getAddress().addr()) + .setPrivatePort(privatePort) + .setPublicPort(publicPort) + .setRuleId(rule.getId()) + .setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT)) + .build(); + FirewallRuleDetailVO ruleDetail = firewallRuleDetailsDao.findDetail(rule.getId(), ApiConstants.FOR_NSX); + if (Arrays.asList(FirewallRule.State.Add, FirewallRule.State.Active).contains(rule.getState())) { + if ((ruleDetail == null && FirewallRule.State.Add == rule.getState()) || (ruleDetail != null && !ruleDetail.getValue().equalsIgnoreCase("true"))) { + logger.debug("Creating port forwarding rule on NSX for VM {} to ports {} - {}", + vm.getUuid(), rule.getDestinationPortStart(), rule.getDestinationPortEnd()); + NsxAnswer answer = nsxService.createPortForwardRule(networkRule); + boolean pfRuleResult = answer.getResult(); + if (pfRuleResult && !answer.isObjectExistent()) { + logger.debug("Port forwarding rule {} created on NSX, adding detail on firewall rules details", rule.getId()); + if (ruleDetail == null && FirewallRule.State.Add == rule.getState()) { + logger.debug("Adding new firewall detail for rule {}", rule.getId()); + firewallRuleDetailsDao.addDetail(rule.getId(), ApiConstants.FOR_NSX, "true", false); + } else { + logger.debug("Updating firewall detail for rule {}", rule.getId()); + ruleDetail.setValue("true"); + firewallRuleDetailsDao.update(ruleDetail.getId(), ruleDetail); + } + } + result &= pfRuleResult; + } + } else if (rule.getState() == FirewallRule.State.Revoke) { + if (ruleDetail == null || (ruleDetail != null && ruleDetail.getValue().equalsIgnoreCase("true"))) { + boolean pfRuleResult = nsxService.deletePortForwardRule(networkRule); + if (pfRuleResult && ruleDetail != null) { + logger.debug("Updating firewall rule detail {} for rule {}, set to false", ruleDetail.getId(), rule.getId()); + ruleDetail.setValue("false"); + firewallRuleDetailsDao.update(ruleDetail.getId(), ruleDetail); + } + result &= pfRuleResult; + } + } + } + return result; + }); + } + @Override public boolean applyPFRules(Network network, List rules) throws ResourceUnavailableException { if (!canHandle(network, Network.Service.PortForwarding)) { return false; } - boolean result = true; - for (PortForwardingRule rule : rules) { - IPAddressVO publicIp = ApiDBUtils.findIpAddressById(rule.getSourceIpAddressId()); - UserVm vm = ApiDBUtils.findUserVmById(rule.getVirtualMachineId()); - if (vm == null && rule.getState() != FirewallRule.State.Revoke) { - continue; - } - NsxOpObject nsxObject = getNsxOpObject(network); - String publicPort = getPublicPortRange(rule); - - String privatePort = getPrivatePFPortRange(rule); - - NsxNetworkRule networkRule = new NsxNetworkRule.Builder() - .setDomainId(nsxObject.getDomainId()) - .setAccountId(nsxObject.getAccountId()) - .setZoneId(nsxObject.getZoneId()) - .setNetworkResourceId(nsxObject.getNetworkResourceId()) - .setNetworkResourceName(nsxObject.getNetworkResourceName()) - .setVpcResource(nsxObject.isVpcResource()) - .setVmId(Objects.nonNull(vm) ? vm.getId() : 0) - .setVmIp(Objects.nonNull(vm) ? vm.getPrivateIpAddress() : null) - .setPublicIp(publicIp.getAddress().addr()) - .setPrivatePort(privatePort) - .setPublicPort(publicPort) - .setRuleId(rule.getId()) - .setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT)) - .build(); - if (Arrays.asList(FirewallRule.State.Add, FirewallRule.State.Active).contains(rule.getState())) { - result &= nsxService.createPortForwardRule(networkRule); - } else if (rule.getState() == FirewallRule.State.Revoke) { - result &= nsxService.deletePortForwardRule(networkRule); - } - } - return result; + return applyPFRulesInternal(network, rules); } public Pair getVpcOrNetwork(Long vpcId, long networkId) { 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 0d556da9f2c0..032967d40617 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 NsxGuestNetworkGuru() { 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 NicProfile allocate(Network network, NicProfile nic, VirtualMachineProfil 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 void createNsxSegment(NetworkVO networkVO, DataCenter zone) { 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 7463a19fd4e5..4df71056601c 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 NicProfile allocate(Network network, NicProfile nic, VirtualMachineProfil 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 NicProfile allocate(Network network, NicProfile nic, VirtualMachineProfil 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/main/java/org/apache/cloudstack/service/NsxServiceImpl.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java index f8880826a3f8..139d8a55e592 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java @@ -19,7 +19,6 @@ import com.cloud.network.IpAddress; import com.cloud.network.Network; import com.cloud.network.nsx.NsxService; -import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.VpcVO; @@ -37,6 +36,8 @@ import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand; import org.apache.cloudstack.agent.api.DeleteNsxNatRuleCommand; import org.apache.cloudstack.agent.api.DeleteNsxTier1GatewayCommand; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.resource.NsxNetworkRule; import org.apache.cloudstack.utils.NsxControllerUtils; import org.apache.cloudstack.utils.NsxHelper; @@ -47,13 +48,11 @@ import java.util.List; import java.util.Objects; -public class NsxServiceImpl implements NsxService { +public class NsxServiceImpl implements NsxService, Configurable { @Inject NsxControllerUtils nsxControllerUtils; @Inject VpcDao vpcDao; - @Inject - NetworkDao networkDao; protected Logger logger = LogManager.getLogger(getClass()); @@ -139,14 +138,13 @@ public boolean deleteStaticNatRule(long zoneId, long domainId, long accountId, L return result.getResult(); } - public boolean createPortForwardRule(NsxNetworkRule netRule) { + public NsxAnswer createPortForwardRule(NsxNetworkRule netRule) { // TODO: if port doesn't exist in default list of services, create a service entry CreateNsxPortForwardRuleCommand createPortForwardCmd = new CreateNsxPortForwardRuleCommand(netRule.getDomainId(), netRule.getAccountId(), netRule.getZoneId(), netRule.getNetworkResourceId(), netRule.getNetworkResourceName(), netRule.isVpcResource(), netRule.getVmId(), netRule.getRuleId(), netRule.getPublicIp(), netRule.getVmIp(), netRule.getPublicPort(), netRule.getPrivatePort(), netRule.getProtocol()); - NsxAnswer result = nsxControllerUtils.sendNsxCommand(createPortForwardCmd, netRule.getZoneId()); - return result.getResult(); + return nsxControllerUtils.sendNsxCommand(createPortForwardCmd, netRule.getZoneId()); } public boolean deletePortForwardRule(NsxNetworkRule netRule) { @@ -190,4 +188,16 @@ public boolean deleteFirewallRules(Network network, List netRule NsxAnswer result = nsxControllerUtils.sendNsxCommand(command, network.getDataCenterId()); return result.getResult(); } + + @Override + public String getConfigComponentName() { + return NsxApiClient.class.getSimpleName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[] { + NSX_API_FAILURE_RETRIES, NSX_API_FAILURE_INTERVAL + }; + } } diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java index e064a6b62916..f44364f34c8b 100644 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java @@ -124,6 +124,9 @@ public static String getServerPoolName(String tier1GatewayName, long lbId) { } public static String getActiveMonitorProfileName(String lbServerPoolName, String port, String protocol) { + if (protocol.equalsIgnoreCase("udp")) { + protocol = "ICMP"; + } return lbServerPoolName + "-" + protocol + "-" + port + "-AM"; } diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxApiClientTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxApiClientTest.java index a0fde08ade8b..fbcca86a28b4 100644 --- a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxApiClientTest.java +++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxApiClientTest.java @@ -17,6 +17,9 @@ package org.apache.cloudstack.service; import com.cloud.network.Network; +import com.vmware.nsx.cluster.Status; +import com.vmware.nsx.model.ClusterStatus; +import com.vmware.nsx.model.ControllerClusterStatus; import com.vmware.nsx_policy.infra.domains.Groups; import com.vmware.nsx_policy.model.Group; import com.vmware.nsx_policy.model.PathExpression; @@ -93,4 +96,16 @@ public void testGetGroupsForTrafficEgress() { Assert.assertEquals(List.of(String.format("%s/%s", NsxApiClient.GROUPS_PATH_PREFIX, segmentName)), sourceGroups); Assert.assertEquals(List.of("ANY"), destinationGroups); } + + @Test + public void testIsNsxControllerActive() { + Status statusService = Mockito.mock(Status.class); + Mockito.when(nsxService.apply(Status.class)).thenReturn(statusService); + ClusterStatus clusterStatus = Mockito.mock(ClusterStatus.class); + ControllerClusterStatus status = Mockito.mock(ControllerClusterStatus.class); + Mockito.when(status.getStatus()).thenReturn("stable"); + Mockito.when(statusService.get()).thenReturn(clusterStatus); + Mockito.when(clusterStatus.getControlClusterStatus()).thenReturn(status); + Assert.assertTrue(client.isNsxControllerActive()); + } } diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxElementTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxElementTest.java index ff7fa5427ee7..7c44a7324fbe 100644 --- a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxElementTest.java +++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxElementTest.java @@ -61,6 +61,7 @@ import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.resource.NsxNetworkRule; +import org.apache.cloudstack.resourcedetail.dao.FirewallRuleDetailsDao; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -124,6 +125,8 @@ public class NsxElementTest { private VpcOfferingServiceMapDao vpcOfferingServiceMapDao; @Mock LoadBalancerVMMapDao lbVmMapDao; + @Mock + FirewallRuleDetailsDao firewallRuleDetailsDao; NsxElement nsxElement; ReservationContext reservationContext; @@ -148,6 +151,7 @@ public void setup() throws NoSuchFieldException, IllegalAccessException { nsxElement.vmInstanceDao = vmInstanceDao; nsxElement.vpcDao = vpcDao; nsxElement.lbVmMapDao = lbVmMapDao; + nsxElement.firewallRuleDetailsDao = firewallRuleDetailsDao; Field field = ApiDBUtils.class.getDeclaredField("s_ipAddressDao"); field.setAccessible(true); 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 66b9684203bd..cb79873f364d 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 void setUp() throws IllegalAccessException, NoSuchFieldException { 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 void testCreateNsxSegmentForIsolatedNetwork() { 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 da21bf11b641..1abef392345e 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 void testAllocate() throws InsufficientVirtualNetworkCapacityException, I 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/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java index cc411543c445..fabe2ab35368 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java @@ -154,8 +154,7 @@ public KVMPhysicalDisk getPhysicalDisk(String name, KVMStoragePool pool) public KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, Storage.StoragePoolType type, Map details) { - logger.debug(String.format( - "Linstor createStoragePool: name: '%s', host: '%s', path: %s, userinfo: %s", name, host, path, userInfo)); + logger.debug("Linstor createStoragePool: name: '{}', host: '{}', path: {}, userinfo: {}", name, host, path, userInfo); LinstorStoragePool storagePool = new LinstorStoragePool(name, host, port, userInfo, type, this); MapStorageUuidToStoragePool.put(name, storagePool); @@ -190,7 +189,7 @@ private void makeResourceAvailable(DevelopersApi api, String rscName, boolean di public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, QemuImg.PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { - logger.debug(String.format("Linstor.createPhysicalDisk: %s;%s", name, format)); + logger.debug("Linstor.createPhysicalDisk: {};{}", name, format); final String rscName = getLinstorRscName(name); LinstorStoragePool lpool = (LinstorStoragePool) pool; final DevelopersApi api = getLinstorAPI(pool); @@ -231,7 +230,7 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, Qemu throw new CloudRuntimeException("Linstor: viewResources didn't return resources or volumes."); } } catch (ApiException apiEx) { - logger.error(String.format("Linstor.createPhysicalDisk: ApiException: %s", apiEx.getBestMessage())); + logger.error("Linstor.createPhysicalDisk: ApiException: {}", apiEx.getBestMessage()); throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx); } } @@ -254,9 +253,8 @@ private void allow2PrimariesIfInUse(DevelopersApi api, String rscName) throws Ap rcm.setOverrideProps(props); ApiCallRcList answers = api.resourceConnectionModify(rscName, inUseNode, localNodeName, rcm); if (answers.hasError()) { - logger.error(String.format( - "Unable to set protocol C and 'allow-two-primaries' on %s/%s/%s", - inUseNode, localNodeName, rscName)); + logger.error("Unable to set protocol C and 'allow-two-primaries' on {}/{}/{}", + inUseNode, localNodeName, rscName); // do not fail here as adding allow-two-primaries property is only a problem while live migrating } } @@ -265,7 +263,7 @@ private void allow2PrimariesIfInUse(DevelopersApi api, String rscName) throws Ap @Override public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map details) { - logger.debug(String.format("Linstor: connectPhysicalDisk %s:%s -> %s", pool.getUuid(), volumePath, details)); + logger.debug("Linstor: connectPhysicalDisk {}:{} -> {}", pool.getUuid(), volumePath, details); if (volumePath == null) { logger.warn("volumePath is null, ignoring"); return false; @@ -304,11 +302,10 @@ private void removeTwoPrimariesRcProps(DevelopersApi api, String inUseNode, Stri rcm.deleteProps(deleteProps); ApiCallRcList answers = api.resourceConnectionModify(rscName, localNodeName, inUseNode, rcm); if (answers.hasError()) { - logger.error( - String.format("Failed to remove 'protocol' and 'allow-two-primaries' on %s/%s/%s: %s", + logger.error("Failed to remove 'protocol' and 'allow-two-primaries' on {}/{}/{}: {}", localNodeName, inUseNode, - rscName, LinstorUtil.getBestErrorMessage(answers))); + rscName, LinstorUtil.getBestErrorMessage(answers)); // do not fail here as removing allow-two-primaries property isn't fatal } } @@ -512,7 +509,7 @@ private boolean resourceSupportZeroBlocks(KVMStoragePool destPool, String resNam @Override public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPools, int timeout, byte[] srcPassphrase, byte[] destPassphrase, Storage.ProvisioningType provisioningType) { - logger.debug(String.format("Linstor.copyPhysicalDisk: %s -> %s", disk.getPath(), name)); + logger.debug("Linstor.copyPhysicalDisk: {} -> {}", disk.getPath(), name); final QemuImg.PhysicalDiskFormat sourceFormat = disk.getFormat(); final String sourcePath = disk.getPath(); @@ -521,7 +518,16 @@ public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMSt final KVMPhysicalDisk dstDisk = destPools.createPhysicalDisk( name, QemuImg.PhysicalDiskFormat.RAW, provisioningType, disk.getVirtualSize(), null); - logger.debug(String.format("Linstor.copyPhysicalDisk: dstPath: %s", dstDisk.getPath())); + final DevelopersApi api = getLinstorAPI(destPools); + final String rscName = LinstorUtil.RSC_PREFIX + name; + try { + LinstorUtil.applyAuxProps(api, rscName, disk.getDispName(), disk.getVmName()); + } catch (ApiException apiExc) { + logger.error("Error setting aux properties for {}", rscName); + logLinstorAnswers(apiExc.getApiCallRcList()); + } + + logger.debug("Linstor.copyPhysicalDisk: dstPath: {}", dstDisk.getPath()); final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath()); destFile.setFormat(dstDisk.getFormat()); destFile.setSize(disk.getVirtualSize()); diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java index 1a2ceb203c95..42f7f82b24f3 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java @@ -26,7 +26,6 @@ import com.linbit.linstor.api.model.ResourceDefinitionCloneRequest; import com.linbit.linstor.api.model.ResourceDefinitionCloneStarted; import com.linbit.linstor.api.model.ResourceDefinitionCreate; -import com.linbit.linstor.api.model.ResourceDefinitionModify; import com.linbit.linstor.api.model.ResourceGroupSpawn; import com.linbit.linstor.api.model.ResourceMakeAvailable; import com.linbit.linstor.api.model.Snapshot; @@ -62,8 +61,8 @@ import com.cloud.storage.DataStoreRole; import com.cloud.storage.ResizeVolumePayload; import com.cloud.storage.SnapshotVO; -import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Storage; +import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.VMTemplateStoragePoolVO; @@ -390,27 +389,6 @@ private void applyQoSSettings(StoragePoolVO storagePool, DevelopersApi api, Stri } } - private void applyAuxProps(DevelopersApi api, String rscName, String dispName, String vmName) - throws ApiException - { - ResourceDefinitionModify rdm = new ResourceDefinitionModify(); - Properties props = new Properties(); - if (dispName != null) - { - props.put("Aux/cs-name", dispName); - } - if (vmName != null) - { - props.put("Aux/cs-vm-name", vmName); - } - if (!props.isEmpty()) - { - rdm.setOverrideProps(props); - ApiCallRcList answers = api.resourceDefinitionModify(rscName, rdm); - checkLinstorAnswersThrow(answers); - } - } - private String getRscGrp(StoragePoolVO storagePoolVO) { return storagePoolVO.getUserInfo() != null && !storagePoolVO.getUserInfo().isEmpty() ? storagePoolVO.getUserInfo() : "DfltRscGrp"; @@ -428,7 +406,8 @@ private String createResourceBase( ApiCallRcList answers = api.resourceGroupSpawn(rscGrp, rscGrpSpawn); checkLinstorAnswersThrow(answers); - applyAuxProps(api, rscName, volName, vmName); + answers = LinstorUtil.applyAuxProps(api, rscName, volName, vmName); + checkLinstorAnswersThrow(answers); return LinstorUtil.getDevicePath(api, rscName); } catch (ApiException apiEx) @@ -499,7 +478,8 @@ private String cloneResource(long csCloneId, VolumeInfo volumeInfo, StoragePoolV if (volumeInfo.getSize() != null && volumeInfo.getSize() > 0) { resizeResource(linstorApi, rscName, volumeInfo.getSize()); } - applyAuxProps(linstorApi, rscName, volumeInfo.getName(), volumeInfo.getAttachedVmName()); + + LinstorUtil.applyAuxProps(linstorApi, rscName, volumeInfo.getName(), volumeInfo.getAttachedVmName()); applyQoSSettings(storagePoolVO, linstorApi, rscName, volumeInfo.getMaxIops()); return LinstorUtil.getDevicePath(linstorApi, rscName); @@ -551,7 +531,7 @@ private String createResourceFromSnapshot(long csSnapshotId, String rscName, Sto answers = linstorApi.resourceSnapshotRestore(cloneRes, snapName, snapshotRestore); checkLinstorAnswersThrow(answers); - applyAuxProps(linstorApi, rscName, volumeVO.getName(), null); + LinstorUtil.applyAuxProps(linstorApi, rscName, volumeVO.getName(), null); applyQoSSettings(storagePoolVO, linstorApi, rscName, volumeVO.getMaxIops()); return LinstorUtil.getDevicePath(linstorApi, rscName); @@ -833,7 +813,7 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal VolumeInfo volume = sinfo.getBaseVolume(); deleteSnapshot( srcData.getDataStore(), - LinstorUtil.RSC_PREFIX + volume.getUuid(), + LinstorUtil.RSC_PREFIX + volume.getPath(), LinstorUtil.RSC_PREFIX + sinfo.getUuid()); } res = new CopyCommandResult(null, answer); @@ -969,7 +949,7 @@ private Answer copyVolume(DataObject srcData, DataObject dstData) { VolumeInfo srcVolInfo = (VolumeInfo) srcData; final StoragePoolVO pool = _storagePoolDao.findById(srcVolInfo.getDataStore().getId()); final DevelopersApi api = LinstorUtil.getLinstorAPI(pool.getHostAddress()); - final String rscName = LinstorUtil.RSC_PREFIX + srcVolInfo.getUuid(); + final String rscName = LinstorUtil.RSC_PREFIX + srcVolInfo.getPath(); VolumeObjectTO to = (VolumeObjectTO) srcVolInfo.getTO(); // patch source format @@ -1082,7 +1062,7 @@ protected Answer copySnapshot(DataObject srcData, DataObject destData) { options.put("volumeSize", snapshotObject.getBaseVolume().getSize() + ""); try { - final String rscName = LinstorUtil.RSC_PREFIX + snapshotObject.getBaseVolume().getUuid(); + final String rscName = LinstorUtil.RSC_PREFIX + snapshotObject.getBaseVolume().getPath(); String snapshotName = setCorrectSnapshotPath(api, rscName, snapshotObject); CopyCommand cmd = new LinstorBackupSnapshotCommand( diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java index 49b8655ed634..d3a8b97cf2a5 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java @@ -22,8 +22,10 @@ import com.linbit.linstor.api.model.ApiCallRc; import com.linbit.linstor.api.model.ApiCallRcList; import com.linbit.linstor.api.model.Node; +import com.linbit.linstor.api.model.Properties; import com.linbit.linstor.api.model.ProviderKind; import com.linbit.linstor.api.model.Resource; +import com.linbit.linstor.api.model.ResourceDefinitionModify; import com.linbit.linstor.api.model.ResourceGroup; import com.linbit.linstor.api.model.ResourceWithVolumes; import com.linbit.linstor.api.model.StoragePool; @@ -240,4 +242,26 @@ public static String getDevicePath(DevelopersApi api, String rscName) throws Api LOGGER.error(errMsg); throw new CloudRuntimeException("Linstor: " + errMsg); } + + public static ApiCallRcList applyAuxProps(DevelopersApi api, String rscName, String dispName, String vmName) + throws ApiException + { + ResourceDefinitionModify rdm = new ResourceDefinitionModify(); + Properties props = new Properties(); + if (dispName != null) + { + props.put("Aux/cs-name", dispName); + } + if (vmName != null) + { + props.put("Aux/cs-vm-name", vmName); + } + ApiCallRcList answers = new ApiCallRcList(); + if (!props.isEmpty()) + { + rdm.setOverrideProps(props); + answers = api.resourceDefinitionModify(rscName, rdm); + } + return answers; + } } diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/snapshot/LinstorVMSnapshotStrategy.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/snapshot/LinstorVMSnapshotStrategy.java index 0fa5e3120f61..c7fe6d211903 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/snapshot/LinstorVMSnapshotStrategy.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/snapshot/LinstorVMSnapshotStrategy.java @@ -240,7 +240,7 @@ public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) { final String snapshotName = vmSnapshotVO.getName(); final List failedToDelete = new ArrayList<>(); for (VolumeObjectTO volumeObjectTO : volumeTOs) { - final String rscName = LinstorUtil.RSC_PREFIX + volumeObjectTO.getUuid(); + final String rscName = LinstorUtil.RSC_PREFIX + volumeObjectTO.getPath(); String err = linstorDeleteSnapshot(api, rscName, snapshotName); if (err != null) @@ -293,7 +293,7 @@ private boolean revertVMSnapshotOperation(VMSnapshot vmSnapshot, long userVmId) final String snapshotName = vmSnapshotVO.getName(); for (VolumeObjectTO volumeObjectTO : volumeTOs) { - final String rscName = LinstorUtil.RSC_PREFIX + volumeObjectTO.getUuid(); + final String rscName = LinstorUtil.RSC_PREFIX + volumeObjectTO.getPath(); String err = linstorRevertSnapshot(api, rscName, snapshotName); if (err != null) { throw new CloudRuntimeException(String.format( diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index a3609e81b377..a169ebc0f19f 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -30,6 +30,7 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; +import com.cloud.cpu.CPU; import org.apache.cloudstack.acl.Role; import org.apache.cloudstack.acl.RoleService; import org.apache.cloudstack.affinity.AffinityGroup; @@ -2286,4 +2287,8 @@ public static SharedFSResponse newSharedFSResponse(ResponseView view, SharedFSJo public static SharedFSJoinVO newSharedFSView(SharedFS sharedFS) { return s_sharedFSJoinDao.newSharedFSView(sharedFS); } + + public static List listZoneClustersArchs(long zoneId) { + return s_clusterDao.getClustersArchsByZone(zoneId); + } } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index e53de0c1d479..810f0abd7e00 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 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.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.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.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.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.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) { @@ -1506,6 +1530,9 @@ public ClusterResponse createClusterResponse(Cluster cluster, Boolean showCapaci clusterResponse.setCpuOvercommitRatio(cpuOvercommitRatio); clusterResponse.setMemoryOvercommitRatio(memoryOvercommitRatio); clusterResponse.setResourceDetails(_clusterDetailsDao.findDetails(cluster.getId())); + if (cluster.getArch() != null) { + clusterResponse.setArch(cluster.getArch().getType()); + } if (showCapacities != null && showCapacities) { List capacities = ApiDBUtils.getCapacityByClusterPodZone(null, null, cluster.getId()); @@ -2389,7 +2416,9 @@ public NetworkOfferingResponse createNetworkOfferingResponse(NetworkOffering off 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 +2521,6 @@ public NetworkResponse createNetworkResponse(ResponseView view, Network network) } } response.setReservedIpRange(reservation); - // return vlan information only to Root admin if (network.getBroadcastUri() != null && view == ResponseView.Full) { String broadcastUri = network.getBroadcastUri().toString(); @@ -2540,6 +2568,13 @@ public NetworkResponse createNetworkResponse(ResponseView view, Network network) 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 +2755,29 @@ public NetworkResponse createNetworkResponse(ResponseView view, Network network) 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 +2842,19 @@ public FirewallResponse createFirewallResponse(FirewallRule fwRule) { 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 +2865,7 @@ public FirewallResponse createFirewallResponse(FirewallRule fwRule) { 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 +3449,11 @@ public VpcResponse createVpcResponse(ResponseView view, Vpc vpc) { 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 +3511,31 @@ public VpcResponse createVpcResponse(ResponseView view, Vpc vpc) { 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 +5377,59 @@ public BucketResponse createBucketResponse(Bucket bucket) { 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/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index a39299d5091e..f4c9b19c1926 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -36,6 +36,7 @@ import javax.inject.Inject; +import com.cloud.cpu.CPU; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker; @@ -3651,7 +3652,7 @@ private List findRelatedDomainIds(Domain domain, boolean isRecursive) { public ListResponse searchForServiceOfferings(ListServiceOfferingsCmd cmd) { Pair, Integer> result = searchForServiceOfferingsInternal(cmd); result.first(); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List offeringResponses = ViewResponseHelper.createServiceOfferingResponse(result.first().toArray(new ServiceOfferingJoinVO[result.first().size()])); response.setResponses(offeringResponses, result.second()); return response; @@ -4476,7 +4477,7 @@ private Pair, Integer> searchForTemplatesInternal(ListTempl null, cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), cmd.getStoragePoolId(), cmd.getImageStoreId(), hypervisorType, showDomr, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedTmpl, cmd.getIds(), parentTemplateId, cmd.getShowUnique(), - templateType, isVnf); + templateType, isVnf, cmd.getArch()); } private Pair, Integer> searchForTemplatesInternal(Long templateId, String name, String keyword, @@ -4485,7 +4486,7 @@ private Pair, Integer> searchForTemplatesInternal(Long temp boolean showDomr, boolean onlyReady, List permittedAccounts, Account caller, ListProjectResourcesCriteria listProjectResourcesCriteria, Map tags, boolean showRemovedTmpl, List ids, Long parentTemplateId, Boolean showUnique, String templateType, - Boolean isVnf) { + Boolean isVnf, CPU.CPUArch arch) { // check if zone is configured, if not, just return empty list List hypers = null; @@ -4523,6 +4524,10 @@ private Pair, Integer> searchForTemplatesInternal(Long temp sc.addAnd("dataStoreId", SearchCriteria.Op.EQ, imageStoreId); } + if (arch != null) { + sc.addAnd("arch", SearchCriteria.Op.EQ, arch); + } + if (storagePoolId != null) { sc.setJoinParameters("storagePool", "pool_id", storagePoolId); } @@ -4912,7 +4917,7 @@ private Pair, Integer> searchForIsosInternal(ListIsosCmd cm return searchForTemplatesInternal(cmd.getId(), cmd.getIsoName(), cmd.getKeyword(), isoFilter, true, cmd.isBootable(), cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), cmd.getStoragePoolId(), cmd.getImageStoreId(), hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, - tags, showRemovedISO, null, null, cmd.getShowUnique(), null, null); + tags, showRemovedISO, null, null, cmd.getShowUnique(), null, null, cmd.getArch()); } @Override diff --git a/server/src/main/java/com/cloud/api/query/dao/AffinityGroupJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/AffinityGroupJoinDaoImpl.java index 2a876ea82265..a5fd2bf11f17 100644 --- a/server/src/main/java/com/cloud/api/query/dao/AffinityGroupJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/AffinityGroupJoinDaoImpl.java @@ -21,21 +21,46 @@ import javax.inject.Inject; - import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.dedicated.DedicatedResourceResponse; import com.cloud.api.ApiResponseHelper; import com.cloud.api.query.vo.AffinityGroupJoinVO; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.DedicatedResources; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DedicatedResourceDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.host.Host; +import com.cloud.host.dao.HostDao; +import com.cloud.org.Cluster; +import com.cloud.user.AccountManager; public class AffinityGroupJoinDaoImpl extends GenericDaoBase implements AffinityGroupJoinDao { @Inject private ConfigurationDao _configDao; + @Inject + private DedicatedResourceDao dedicatedResourceDao; + @Inject + private DataCenterDao dataCenterDao; + @Inject + private HostPodDao podDao; + @Inject + private ClusterDao clusterDao; + @Inject + private HostDao hostDao; + @Inject + private AccountManager accountManager; private final SearchBuilder agSearch; @@ -64,6 +89,14 @@ public AffinityGroupResponse newAffinityGroupResponse(AffinityGroupJoinVO vag) { ApiResponseHelper.populateOwner(agResponse, vag); + Long callerId = CallContext.current().getCallingAccountId(); + boolean isCallerRootAdmin = accountManager.isRootAdmin(callerId); + boolean containsDedicatedResources = vag.getType().equals("ExplicitDedication"); + if (isCallerRootAdmin && containsDedicatedResources) { + List dedicatedResources = dedicatedResourceDao.listByAffinityGroupId(vag.getId()); + this.populateDedicatedResourcesField(dedicatedResources, agResponse); + } + // update vm information long instanceId = vag.getVmId(); if (instanceId > 0) { @@ -76,6 +109,32 @@ public AffinityGroupResponse newAffinityGroupResponse(AffinityGroupJoinVO vag) { return agResponse; } + private void populateDedicatedResourcesField(List dedicatedResources, AffinityGroupResponse agResponse) { + if (dedicatedResources.isEmpty()) { + return; + } + + for (DedicatedResourceVO resource : dedicatedResources) { + DedicatedResourceResponse dedicatedResourceResponse = null; + + if (resource.getDataCenterId() != null) { + DataCenter dataCenter = dataCenterDao.findById(resource.getDataCenterId()); + dedicatedResourceResponse = new DedicatedResourceResponse(dataCenter.getUuid(), dataCenter.getName(), DedicatedResources.Type.Zone); + } else if (resource.getPodId() != null) { + HostPodVO pod = podDao.findById(resource.getPodId()); + dedicatedResourceResponse = new DedicatedResourceResponse(pod.getUuid(), pod.getName(), DedicatedResources.Type.Pod); + } else if (resource.getClusterId() != null) { + Cluster cluster = clusterDao.findById(resource.getClusterId()); + dedicatedResourceResponse = new DedicatedResourceResponse(cluster.getUuid(), cluster.getName(), DedicatedResources.Type.Cluster); + } else if (resource.getHostId() != null) { + Host host = hostDao.findById(resource.getHostId()); + dedicatedResourceResponse = new DedicatedResourceResponse(host.getUuid(), host.getName(), DedicatedResources.Type.Host); + } + + agResponse.addDedicatedResource(dedicatedResourceResponse); + } + } + @Override public AffinityGroupResponse setAffinityGroupResponse(AffinityGroupResponse vagData, AffinityGroupJoinVO vag) { // update vm information 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 902c9c9bb25c..2b3be728bd38 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,13 @@ import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import javax.inject.Inject; +import com.cloud.cpu.CPU; +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; @@ -30,6 +34,7 @@ import org.apache.cloudstack.api.response.ResourceTagResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; import org.springframework.stereotype.Component; @@ -56,6 +61,8 @@ public class DataCenterJoinDaoImpl extends GenericDaoBase clusterArchs = ApiDBUtils.listZoneClustersArchs(dataCenter.getId()); + zoneResponse.setMultiArch(CollectionUtils.isNotEmpty(clusterArchs) && clusterArchs.size() > 1); + + List 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/DiskOfferingJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java index 8076755bb715..e53f94fa0c3e 100644 --- a/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java @@ -38,11 +38,13 @@ import com.cloud.offering.ServiceOffering; import com.cloud.server.ResourceTag; import com.cloud.user.AccountManager; -import com.cloud.utils.db.Attribute; +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 static org.apache.cloudstack.query.QueryService.SortKeyAscending; + @Component public class DiskOfferingJoinDaoImpl extends GenericDaoBase implements DiskOfferingJoinDao { @@ -57,7 +59,6 @@ public class DiskOfferingJoinDaoImpl extends GenericDaoBase dofIdSearch; private SearchBuilder diskOfferingSearch; - private final Attribute _typeAttr; protected DiskOfferingJoinDaoImpl() { @@ -69,9 +70,6 @@ protected DiskOfferingJoinDaoImpl() { diskOfferingSearch.and("idIN", diskOfferingSearch.entity().getId(), SearchCriteria.Op.IN); diskOfferingSearch.done(); - - _typeAttr = _allAttributes.get("type"); - _count = "select count(distinct id) from disk_offering_view WHERE "; } @@ -166,6 +164,8 @@ public DiskOfferingJoinVO newDiskOfferingView(DiskOffering offering) { @Override public List searchByIds(Long... offeringIds) { + Filter searchFilter = new Filter(DiskOfferingJoinVO.class, "sortKey", SortKeyAscending.value()); + searchFilter.addOrderBy(DiskOfferingJoinVO.class, "id", true); // set detail batch query size int DETAILS_BATCH_SIZE = 2000; String batchCfg = configDao.getValue("detail.batch.query.size"); @@ -184,7 +184,7 @@ public List searchByIds(Long... offeringIds) { } SearchCriteria sc = diskOfferingSearch.create(); sc.setParameters("idIN", ids); - List accounts = searchIncludingRemoved(sc, null, null, false); + List accounts = searchIncludingRemoved(sc, searchFilter, null, false); if (accounts != null) { uvList.addAll(accounts); } @@ -200,7 +200,7 @@ public List searchByIds(Long... offeringIds) { } SearchCriteria sc = diskOfferingSearch.create(); sc.setParameters("idIN", ids); - List accounts = searchIncludingRemoved(sc, null, null, false); + List accounts = searchIncludingRemoved(sc, searchFilter, null, false); if (accounts != null) { uvList.addAll(accounts); } diff --git a/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java index 97ab3b4f07f3..59de277aa4b9 100644 --- a/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java @@ -209,6 +209,9 @@ public HostResponse newHostResponse(HostJoinVO host, EnumSet detail hostResponse.setImplicitHostTags(host.getImplicitTag()); hostResponse.setHypervisorVersion(host.getHypervisorVersion()); + if (host.getArch() != null) { + hostResponse.setArch(host.getArch().getType()); + } float cpuWithOverprovisioning = host.getCpus() * host.getSpeed() * cpuOverprovisioningFactor; hostResponse.setCpuAllocatedValue(cpu); 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 3d50b88846c1..22b51f6fd8c5 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 NetworkOfferingResponse newNetworkOfferingResponse(NetworkOffering offeri } networkOfferingResponse.setInternetProtocol(protocol); } + if (offering.getRoutingMode() != null) { + networkOfferingResponse.setRoutingMode(offering.getRoutingMode().toString()); + } + if (offering.isSpecifyAsNumber() != null) { + networkOfferingResponse.setSpecifyAsNumber(offering.isSpecifyAsNumber()); + } networkOfferingResponse.setObjectName("networkoffering"); return networkOfferingResponse; diff --git a/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDao.java b/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDao.java index d28b9fc7e88f..1c9efd37d661 100644 --- a/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDao.java +++ b/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDao.java @@ -34,5 +34,6 @@ public interface ServiceOfferingJoinDao extends GenericDao> listDomainsOfServiceOfferingsUsedByDomainPath(String domainPath); + List searchByIds(Long... id); } diff --git a/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java index 48ccc356f1a6..d3c7a7decdea 100644 --- a/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java @@ -45,10 +45,13 @@ import com.cloud.storage.DiskOfferingVO; import com.cloud.user.AccountManager; import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; +import static org.apache.cloudstack.query.QueryService.SortKeyAscending; + @Component public class ServiceOfferingJoinDaoImpl extends GenericDaoBase implements ServiceOfferingJoinDao { @@ -233,6 +236,9 @@ public Map> listDomainsOfServiceOfferingsUsedByDomainPath(Str @Override public List searchByIds(Long... offeringIds) { + Filter searchFilter = new Filter(ServiceOfferingJoinVO.class, "sortKey", SortKeyAscending.value()); + searchFilter.addOrderBy(ServiceOfferingJoinVO.class, "id", true); + // set detail batch query size int DETAILS_BATCH_SIZE = 2000; String batchCfg = configDao.getValue("detail.batch.query.size"); @@ -251,9 +257,9 @@ public List searchByIds(Long... offeringIds) { } SearchCriteria sc = srvOfferingSearch.create(); sc.setParameters("idIN", ids); - List accounts = searchIncludingRemoved(sc, null, null, false); - if (accounts != null) { - uvList.addAll(accounts); + List offerings = searchIncludingRemoved(sc, searchFilter, null, false); + if (offerings != null) { + uvList.addAll(offerings); } curr_index += DETAILS_BATCH_SIZE; } @@ -267,9 +273,9 @@ public List searchByIds(Long... offeringIds) { } SearchCriteria sc = srvOfferingSearch.create(); sc.setParameters("idIN", ids); - List accounts = searchIncludingRemoved(sc, null, null, false); - if (accounts != null) { - uvList.addAll(accounts); + List offerings = searchIncludingRemoved(sc, searchFilter, null, false); + if (offerings != null) { + uvList.addAll(offerings); } } return uvList; diff --git a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java index cb0f0126ab87..0bdf5040c82f 100644 --- a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java @@ -332,6 +332,9 @@ public TemplateResponse newTemplateResponse(EnumSet templateResponse.setDirectDownload(template.isDirectDownload()); templateResponse.setDeployAsIs(template.isDeployAsIs()); templateResponse.setRequiresHvm(template.isRequiresHvm()); + if (template.getArch() != null) { + templateResponse.setArch(template.getArch().getType()); + } //set template children disks Set childTemplatesSet = new HashSet(); @@ -600,6 +603,9 @@ public TemplateResponse newIsoResponse(TemplateJoinVO iso, ResponseView view) { _accountService.isRootAdmin(CallContext.current().getCallingAccount().getId()))); isoResponse.setDirectDownload(iso.isDirectDownload()); + if (iso.getArch() != null) { + isoResponse.setArch(iso.getArch().getType()); + } isoResponse.setObjectName("iso"); return isoResponse; diff --git a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index bd97178e8a85..af26a242db47 100644 --- a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java @@ -426,6 +426,12 @@ public UserVmResponse newUserVmResponse(ResponseView view, String objectName, Us userVmResponse.setDynamicallyScalable(userVm.isDynamicallyScalable()); } + if (userVm.getDeleteProtection() == null) { + userVmResponse.setDeleteProtection(false); + } else { + userVmResponse.setDeleteProtection(userVm.getDeleteProtection()); + } + if (userVm.getAutoScaleVmGroupName() != null) { userVmResponse.setAutoScaleVmGroupName(userVm.getAutoScaleVmGroupName()); } diff --git a/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java index 2becc05eaa01..4f5d984c969a 100644 --- a/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java @@ -138,6 +138,12 @@ public VolumeResponse newVolumeResponse(ResponseView view, VolumeJoinVO volume) volResponse.setMinIops(volume.getMinIops()); volResponse.setMaxIops(volume.getMaxIops()); + if (volume.getDeleteProtection() == null) { + volResponse.setDeleteProtection(false); + } else { + volResponse.setDeleteProtection(volume.getDeleteProtection()); + } + volResponse.setCreated(volume.getCreated()); if (volume.getState() != null) { volResponse.setState(volume.getState().toString()); diff --git a/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDaoImpl.java index 11810709ccbd..afa5c206f0f3 100644 --- a/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/VpcOfferingJoinDaoImpl.java @@ -64,6 +64,12 @@ public VpcOfferingResponse newVpcOfferingResponse(VpcOffering offering) { offeringResponse.setSupportsDistributedRouter(offering.isSupportsDistributedRouter()); offeringResponse.setSupportsRegionLevelVpc(offering.isOffersRegionLevelVPC()); offeringResponse.setCreated(offering.getCreated()); + if (offering.getRoutingMode() != null) { + offeringResponse.setRoutingMode(offering.getRoutingMode().toString()); + } + if (offering.isSpecifyAsNumber() != null) { + offeringResponse.setSpecifyAsNumber(offering.isSpecifyAsNumber()); + } if (offering instanceof VpcOfferingJoinVO) { VpcOfferingJoinVO offeringJoinVO = (VpcOfferingJoinVO) offering; offeringResponse.setDomainId(offeringJoinVO.getDomainUuid()); @@ -71,7 +77,9 @@ public VpcOfferingResponse newVpcOfferingResponse(VpcOffering offering) { offeringResponse.setZoneId(offeringJoinVO.getZoneUuid()); offeringResponse.setZone(offeringJoinVO.getZoneName()); offeringResponse.setForNsx(offeringJoinVO.isForNsx()); - offeringResponse.setNsxMode(offeringJoinVO.getNsxMode()); + if (offeringJoinVO.getNetworkMode() != null) { + offeringResponse.setNetworkMode(offeringJoinVO.getNetworkMode().name()); + } String protocol = offeringJoinVO.getInternetProtocol(); if (StringUtils.isEmpty(protocol)) { protocol = NetUtils.InternetProtocol.IPv4.toString(); diff --git a/server/src/main/java/com/cloud/api/query/vo/HostJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/HostJoinVO.java index 4c5fa20f822d..72918c3fa274 100644 --- a/server/src/main/java/com/cloud/api/query/vo/HostJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/HostJoinVO.java @@ -29,6 +29,7 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; +import com.cloud.cpu.CPU; import com.cloud.host.Host.Type; import com.cloud.host.Status; import com.cloud.hypervisor.Hypervisor.HypervisorType; @@ -39,6 +40,7 @@ import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.ha.HAConfig; import org.apache.cloudstack.outofbandmanagement.OutOfBandManagement; +import org.apache.cloudstack.util.CPUArchConverter; import org.apache.cloudstack.util.HypervisorTypeConverter; import org.apache.commons.lang3.StringUtils; @@ -213,6 +215,10 @@ public class HostJoinVO extends BaseViewVO implements InternalIdentity, Identity @Column(name = "username") private String username; + @Column(name = "arch") + @Convert(converter = CPUArchConverter.class) + private CPU.CPUArch arch; + @Override public long getId() { return this.id; @@ -432,4 +438,8 @@ public boolean isInMaintenanceStates() { ResourceState.Maintenance, ResourceState.ErrorInMaintenance, ResourceState.PrepareForMaintenance) .contains(getResourceState()); } + + public CPU.CPUArch getArch() { + return arch; + } } diff --git a/server/src/main/java/com/cloud/api/query/vo/NetworkOfferingJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/NetworkOfferingJoinVO.java index edae63ff7eb4..4ed54de80424 100644 --- a/server/src/main/java/com/cloud/api/query/vo/NetworkOfferingJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/NetworkOfferingJoinVO.java @@ -160,8 +160,8 @@ public class NetworkOfferingJoinVO extends BaseViewVO implements NetworkOffering @Column(name = "for_nsx") boolean forNsx; - @Column(name = "nsx_mode") - String nsxMode; + @Column(name = "network_mode") + NetworkMode networkMode; @Column(name = "service_package_id") private String servicePackageUuid = null; @@ -190,6 +190,13 @@ public class NetworkOfferingJoinVO extends BaseViewVO implements NetworkOffering @Column(name = "internet_protocol") private String internetProtocol = null; + @Column(name="routing_mode") + @Enumerated(value = EnumType.STRING) + private RoutingMode routingMode; + + @Column(name = "specify_as_number") + private Boolean specifyAsNumber; + public NetworkOfferingJoinVO() { } @@ -365,12 +372,12 @@ public void setForNsx(boolean forNsx) { } @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; } public String getServicePackage() { @@ -441,4 +448,21 @@ public String getInternetProtocol() { 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/server/src/main/java/com/cloud/api/query/vo/TemplateJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/TemplateJoinVO.java index babc5ac55676..cd1496f65b1a 100644 --- a/server/src/main/java/com/cloud/api/query/vo/TemplateJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/TemplateJoinVO.java @@ -27,6 +27,7 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; +import com.cloud.cpu.CPU; import com.cloud.user.Account; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; @@ -37,6 +38,7 @@ import com.cloud.template.VirtualMachineTemplate; import com.cloud.template.VirtualMachineTemplate.State; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.util.CPUArchConverter; import org.apache.cloudstack.util.HypervisorTypeConverter; @Entity @@ -70,6 +72,10 @@ public class TemplateJoinVO extends BaseViewWithTagInformationVO implements Cont @Column(name = "hvm") private boolean requiresHvm; + @Column(name = "arch") + @Convert(converter = CPUArchConverter.class) + private CPU.CPUArch arch; + @Column(name = "bits") private int bits; @@ -543,4 +549,8 @@ public String getUserDataPolicy() { public String getUserDataParams() { return userDataParams; } + + public CPU.CPUArch getArch() { + return arch; + } } diff --git a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java index accf8e688d9d..701fa7d4f826 100644 --- a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java @@ -436,6 +436,9 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro @Column(name = "dynamically_scalable") private boolean isDynamicallyScalable; + @Column(name = "delete_protection") + protected Boolean deleteProtection; + public UserVmJoinVO() { // Empty constructor @@ -946,6 +949,9 @@ public Boolean isDynamicallyScalable() { return isDynamicallyScalable; } + public Boolean getDeleteProtection() { + return deleteProtection; + } @Override public Class getEntityType() { diff --git a/server/src/main/java/com/cloud/api/query/vo/VolumeJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/VolumeJoinVO.java index 79f558a3ef50..2ae720fa8524 100644 --- a/server/src/main/java/com/cloud/api/query/vo/VolumeJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/VolumeJoinVO.java @@ -280,6 +280,9 @@ public class VolumeJoinVO extends BaseViewWithTagInformationVO implements Contro @Column(name = "encrypt_format") private String encryptionFormat = null; + @Column(name = "delete_protection") + protected Boolean deleteProtection; + public VolumeJoinVO() { } @@ -619,6 +622,10 @@ public String getEncryptionFormat() { return encryptionFormat; } + public Boolean getDeleteProtection() { + return deleteProtection; + } + @Override public Class getEntityType() { return Volume.class; diff --git a/server/src/main/java/com/cloud/api/query/vo/VpcOfferingJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/VpcOfferingJoinVO.java index 215c94dea251..d72f5b619071 100644 --- a/server/src/main/java/com/cloud/api/query/vo/VpcOfferingJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/VpcOfferingJoinVO.java @@ -27,6 +27,7 @@ import javax.persistence.Table; import com.cloud.network.vpc.VpcOffering; +import com.cloud.offering.NetworkOffering; import com.cloud.utils.db.GenericDao; @Entity @@ -80,8 +81,8 @@ public class VpcOfferingJoinVO implements VpcOffering { @Column(name = "for_nsx") boolean forNsx = false; - @Column(name = "nsx_mode") - String nsxMode; + @Column(name = "network_mode") + NetworkOffering.NetworkMode networkMode; @Column(name = "domain_id") private String domainId; @@ -107,6 +108,13 @@ public class VpcOfferingJoinVO implements VpcOffering { @Column(name = "internet_protocol") private String internetProtocol = null; + @Column(name="routing_mode") + @Enumerated(value = EnumType.STRING) + private NetworkOffering.RoutingMode routingMode; + + @Column(name = "specify_as_number") + private Boolean specifyAsNumber = false; + public VpcOfferingJoinVO() { } @@ -150,8 +158,8 @@ public boolean isForNsx() { } @Override - public String getNsxMode() { - return nsxMode; + public NetworkOffering.NetworkMode getNetworkMode() { + return networkMode; } @Override @@ -164,6 +172,24 @@ public Date getCreated() { return created; } + @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; + } + @Override public Long getServiceOfferingId() { return serviceOfferingId; diff --git a/server/src/main/java/com/cloud/bgp/BGPServiceImpl.java b/server/src/main/java/com/cloud/bgp/BGPServiceImpl.java new file mode 100644 index 000000000000..0e6ae7ade1a2 --- /dev/null +++ b/server/src/main/java/com/cloud/bgp/BGPServiceImpl.java @@ -0,0 +1,438 @@ +// 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.dc.ASNumberRangeVO; +import com.cloud.dc.ASNumberVO; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.ASNumberDao; +import com.cloud.dc.dao.ASNumberRangeDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.domain.Domain; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.NetworkModel; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkServiceMapDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.element.BgpServiceProvider; +import com.cloud.network.element.NetworkElement; +import com.cloud.network.vpc.Vpc; +import com.cloud.network.vpc.VpcOfferingVO; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.network.vpc.dao.VpcOfferingDao; +import com.cloud.network.vpc.dao.VpcServiceMapDao; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; +import com.cloud.user.Account; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.Pair; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import com.cloud.utils.db.TransactionCallbackNoReturn; +import com.cloud.utils.db.TransactionStatus; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.api.command.user.bgp.ListASNumbersCmd; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.network.BgpPeerVO; +import org.apache.cloudstack.network.RoutedIpv4Manager; +import org.apache.cloudstack.network.dao.BgpPeerDao; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.inject.Inject; +import java.security.InvalidParameterException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class BGPServiceImpl implements BGPService { + + public static final Logger LOGGER = LogManager.getLogger(BGPServiceImpl.class); + + @Inject + private DataCenterDao dataCenterDao; + @Inject + private ASNumberRangeDao asNumberRangeDao; + @Inject + private ASNumberDao asNumberDao; + @Inject + private NetworkDao networkDao; + @Inject + private VpcDao vpcDao; + @Inject + private VpcOfferingDao vpcOfferingDao; + @Inject + private NetworkOfferingDao networkOfferingDao; + @Inject + private AccountDao accountDao; + @Inject + private DomainDao domainDao; + @Inject + NetworkServiceMapDao ntwkSrvcDao; + @Inject + NetworkModel networkModel; + @Inject + BgpPeerDao bgpPeerDao; + @Inject + RoutedIpv4Manager routedIpv4Manager; + @Inject + VpcServiceMapDao vpcServiceMapDao; + + public BGPServiceImpl() { + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_AS_RANGE_CREATE, eventDescription = "AS Range creation") + public ASNumberRange createASNumberRange(long zoneId, long startASNumber, long endASNumber) { + DataCenterVO zone = dataCenterDao.findById(zoneId); + if (zone == null) { + String msg = String.format("Cannot find a zone with ID %s", zoneId); + LOGGER.error(msg); + throw new InvalidParameterException(msg); + } + if (startASNumber > 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 25c866d86092..25cbd10de0a4 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.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"; @@ -475,9 +477,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati private long _defaultPageSize = Long.parseLong(Config.DefaultPageSize.getDefaultValue()); private static final String DOMAIN_NAME_PATTERN = "^((?!-)[A-Za-z0-9-]{1,63}(? configValuesForValidation = new HashSet(); - private Set weightBasedParametersForValidation = new HashSet(); - private Set overprovisioningFactorsForValidation = new HashSet(); + private Set configValuesForValidation = new HashSet<>(); + private Set weightBasedParametersForValidation = new HashSet<>(); + private Set overprovisioningFactorsForValidation = new HashSet<>(); public static final ConfigKey SystemVMUseLocalStorage = new ConfigKey(Boolean.class, "system.vm.use.local.storage", "Advanced", "false", "Indicates whether to use local storage pools or shared storage pools for system VMs.", false, ConfigKey.Scope.Zone, null); @@ -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 @@ -574,7 +577,7 @@ protected void populateConfigValuesForValidationSet() { configValuesForValidation.add(UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout.key()); } - private void weightBasedParametersForValidation() { + protected void weightBasedParametersForValidation() { weightBasedParametersForValidation.add(AlertManager.CPUCapacityThreshold.key()); weightBasedParametersForValidation.add(AlertManager.StorageAllocatedCapacityThreshold.key()); weightBasedParametersForValidation.add(AlertManager.StorageCapacityThreshold.key()); @@ -596,7 +599,7 @@ private void weightBasedParametersForValidation() { weightBasedParametersForValidation.add(ClusterDrsService.ClusterDrsImbalanceSkipThreshold.key()); } - private void overProvisioningFactorsForValidation() { + protected void overProvisioningFactorsForValidation() { overprovisioningFactorsForValidation.add(CapacityManager.MemOverprovisioningFactor.key()); overprovisioningFactorsForValidation.add(CapacityManager.CpuOverprovisioningFactor.key()); overprovisioningFactorsForValidation.add(CapacityManager.StorageOverprovisioningFactor.key()); @@ -646,7 +649,7 @@ protected void validateIpAddressRelatedConfigValues(final String configName, fin } } if (err) { - throw new InvalidParameterValueException("Invalid IP address value(s) specified for the config value"); + throw new InvalidParameterValueException("Invalid IP address value(s) specified for the config value."); } } @@ -688,9 +691,8 @@ public boolean stop() { @DB public String updateConfiguration(final long userId, final String name, final String category, String value, final String scope, final Long resourceId) { final String validationMsg = validateConfigurationValue(name, value, scope); - if (validationMsg != null) { - logger.error("Invalid configuration option, name: " + name + ", value:" + value); + logger.error("Invalid value [{}] for configuration [{}] due to [{}].", value, name, validationMsg); throw new InvalidParameterValueException(validationMsg); } @@ -953,8 +955,6 @@ public Configuration updateConfiguration(final UpdateCfgCmd cmd) throws InvalidP category = config.getCategory(); } - validateIpAddressRelatedConfigValues(name, value); - if (value == null) { return _configDao.findByName(name); } @@ -1189,14 +1189,21 @@ public Pair resetConfiguration(final ResetCfgCmd cmd) thr return new Pair(_configDao.findByName(name), newValue); } - protected String validateConfigurationValue(final String name, String value, final String scope) { + /** + * Validates whether a value is valid for the specified configuration. This includes type and range validation. + * @param name name of the configuration. + * @param value value to validate. + * @param scope scope of the configuration. + * @return null if the value is valid; otherwise, returns an error message. + */ + protected String validateConfigurationValue(String name, String value, String scope) { final ConfigurationVO cfg = _configDao.findByName(name); if (cfg == null) { logger.error("Missing configuration variable " + name + " in configuration table"); return "Invalid configuration variable."; } - final String configScope = cfg.getScope(); + String configScope = cfg.getScope(); if (scope != null) { if (!configScope.contains(scope) && !(ENABLE_ACCOUNT_SETTINGS_FOR_DOMAIN.value() && configScope.contains(ConfigKey.Scope.Account.toString()) && @@ -1205,11 +1212,11 @@ protected String validateConfigurationValue(final String name, String value, fin return "Invalid scope id provided for the parameter " + name; } } - Class type = null; - final Config configuration = Config.getConfig(name); + Class type; + Config configuration = Config.getConfig(name); if (configuration == null) { logger.warn("Did not find configuration " + name + " in Config.java. Perhaps moved to ConfigDepot"); - final ConfigKey configKey = _configDepot.get(name); + ConfigKey configKey = _configDepot.get(name); if(configKey == null) { logger.warn("Did not find configuration " + name + " in ConfigDepot too."); return null; @@ -1218,144 +1225,161 @@ protected String validateConfigurationValue(final String name, String value, fin } else { type = configuration.getType(); } - //no need to validate further if a - //config can have null value. - String errMsg = null; - try { - if (type.equals(Integer.class)) { - errMsg = "There was error in trying to parse value: " + value + ". Please enter a valid integer value for parameter " + name; - Integer.parseInt(value); - } else if (type.equals(Float.class)) { - errMsg = "There was error in trying to parse value: " + value + ". Please enter a valid float value for parameter " + name; - Float.parseFloat(value); - } else if (type.equals(Long.class)) { - errMsg = "There was error in trying to parse value: " + value + ". Please enter a valid long value for parameter " + name; - Long.parseLong(value); - } - } catch (final Exception e) { - // catching generic exception as some throws NullPointerException and some throws NumberFormatExcpeion - logger.error(errMsg); - return errMsg; + + boolean isTypeValid = validateValueType(value, type); + if (!isTypeValid) { + return String.format("Value [%s] is not a valid [%s].", value, type); } - if (value == null) { - if (type.equals(Boolean.class)) { - return "Please enter either 'true' or 'false'."; - } - if (overprovisioningFactorsForValidation.contains(name)) { - final String msg = "value cannot be null for the parameter " + name; - logger.error(msg); - return msg; - } - return null; + return validateValueRange(name, value, type, configuration); + } + + /** + * Returns whether a value is valid for a configuration of the provided type. + * Valid configuration values are: + * + *
    + *
  • String: any value, including null;
  • + *
  • Character: any value, including null;
  • + *
  • Boolean: strings that equal "true" or "false" (case-sensitive);
  • + *
  • Integer, Short, Long: strings that contain a valid int/short/long;
  • + *
  • Float, Double: strings that contain a valid float/double, except infinity.
  • + *
+ * + * If a type isn't listed here, then the value will be considered invalid. + * @param value value to validate. + * @param type type of the configuration. + * @return boolean indicating whether the value is valid. + */ + protected boolean validateValueType(String value, Class type) { + if (type == String.class || type == Character.class) { + return true; } - value = value.trim(); try { - if (overprovisioningFactorsForValidation.contains(name) && Float.parseFloat(value) <= 0f) { - final String msg = name + " should be greater than 0"; - logger.error(msg); - throw new InvalidParameterValueException(msg); + if (type == Boolean.class) { + return value.equals("true") || value.equals("false"); + } else if (type == Integer.class) { + Integer.parseInt(value); + } else if (type == Long.class) { + Long.parseLong(value); + } else if (type == Short.class) { + Short.parseShort(value); + } else if (type == Float.class) { + float floatValue = Float.parseFloat(value); + return !Float.isInfinite(floatValue); + } else if (type == Double.class) { + double doubleValue = Double.parseDouble(value); + return !Double.isInfinite(doubleValue); + } else { + return false; } - } catch (final NumberFormatException e) { - final String msg = "There was an error trying to parse the float value for: " + name; - logger.error(msg); - throw new InvalidParameterValueException(msg); + return true; + } catch (NullPointerException | NumberFormatException e) { + return false; } + } - if (type.equals(Boolean.class)) { - if (!(value.equals("true") || value.equals("false"))) { - logger.error("Configuration variable " + name + " is expecting true or false instead of " + value); - return "Please enter either 'true' or 'false'."; + /** + * If the specified configuration contains a range, validates if the value is in that range. If it doesn't contain + * a range, any value is considered valid. + * The value must be previously checked by `validateValueType` so there aren't casting exceptions here. + * @param name name of the configuration. + * @param value value to validate. + * @param type type of the value. + * @param configuration if the configuration uses Config instead of ConfigKey, the Config object; null otherwise. + * @return if the value is valid, returns null; if not, returns an error message. + */ + protected String validateValueRange(String name, String value, Class type, Config configuration) { + if (type.equals(Float.class)) { + Float val = Float.parseFloat(value); + if (overprovisioningFactorsForValidation.contains(name) && val <= 0f) { + return String.format("Value for configuration [%s] should be greater than 0.", name); + } else if (weightBasedParametersForValidation.contains(name) && (val < 0f || val > 1f)) { + return String.format("Please enter a value between 0 and 1 for the configuration parameter: [%s].", name); } - return null; } if (type.equals(Integer.class)) { - try { - final int val = Integer.parseInt(value); - if (NetworkModel.MACIdentifier.key().equalsIgnoreCase(name)) { - //The value need to be between 0 to 255 because the mac generation needs a value of 8 bit - //0 value is considered as disable. - if(val < 0 || val > 255){ - throw new InvalidParameterValueException(name + " value should be between 0 and 255. 0 value will disable this feature"); - } + int val = Integer.parseInt(value); + if (NetworkModel.MACIdentifier.key().equalsIgnoreCase(name)) { + // The value needs to be between 0 to 255 because the MAC generation needs a value of 8 bits + // 0 is considered as disabled. + if (val < 0 || val > 255){ + return String.format("[%s] value should be between 0 and 255. 0 value will disable this feature.", name); } - - if (UnmanagedVMsManager.ThreadsOnMSToImportVMwareVMFiles.key().equalsIgnoreCase(name) || UnmanagedVMsManager.ThreadsOnKVMHostToImportVMwareVMFiles.key().equalsIgnoreCase(name)) { - if (val > 10) { - throw new InvalidParameterValueException("Please enter a value between 0 and 10 for the configuration parameter: " + name + ", -1 will disable it"); - } + } + if (UnmanagedVMsManager.ThreadsOnMSToImportVMwareVMFiles.key().equalsIgnoreCase(name) || + UnmanagedVMsManager.ThreadsOnKVMHostToImportVMwareVMFiles.key().equalsIgnoreCase(name)) { + if (val < -1 || val > 10) { + return String.format("Please enter a value between -1 and 10 for the configuration parameter: [%s]. -1 will disable it.", name); } - - if (configValuesForValidation.contains(name)) { - if (val <= 0) { - throw new InvalidParameterValueException("Please enter a positive value for the configuration parameter:" + name); - } - if ("vm.password.length".equalsIgnoreCase(name) && val < 6) { - throw new InvalidParameterValueException("Please enter a value greater than 5 for the configuration parameter:" + name); - } - if ("remote.access.vpn.psk.length".equalsIgnoreCase(name)) { - if (val < 8) { - throw new InvalidParameterValueException("Please enter a value greater than 7 for the configuration parameter:" + name); - } - if (val > 256) { - throw new InvalidParameterValueException("Please enter a value less than 257 for the configuration parameter:" + name); - } - } - if (UserDataManager.VM_USERDATA_MAX_LENGTH_STRING.equalsIgnoreCase(name)) { - if (val > 1048576) { - throw new InvalidParameterValueException("Please enter a value less than 1048576 for the configuration parameter:" + name); - } - } + } else if (configValuesForValidation.contains(name)) { + if (val <= 0) { + return String.format("Please enter a positive value for the configuration parameter: [%s].", name); } - } catch (final NumberFormatException e) { - logger.error("There was an error trying to parse the integer value for configuration parameter: " + name); - throw new InvalidParameterValueException("There was an error trying to parse the integer value for configuration parameter: " + name); - } - } - - if (type.equals(Float.class)) { - try { - final Float val = Float.parseFloat(value); - if (weightBasedParametersForValidation.contains(name) && (val < 0f || val > 1f)) { - throw new InvalidParameterValueException("Please enter a value between 0 and 1 for the configuration parameter: " + name); + if ("vm.password.length".equalsIgnoreCase(name) && val < 6) { + return String.format("Please enter a value greater than 5 for the configuration parameter: [%s].", name); + } + if ("remote.access.vpn.psk.length".equalsIgnoreCase(name) && (val < 8 || val > 256)) { + return String.format("Please enter a value greater than 7 and less than 257 for the configuration parameter: [%s].", name); + } + if (UserDataManager.VM_USERDATA_MAX_LENGTH_STRING.equalsIgnoreCase(name) && val > 1048576) { + return String.format("Please enter a value less than 1048577 for the configuration parameter: [%s].", name); } - } catch (final NumberFormatException e) { - logger.error("There was an error trying to parse the float value for configuration parameter: " + name); - throw new InvalidParameterValueException("There was an error trying to parse the float value for configuration parameter: " + name); } } if (type.equals(String.class)) { - if (name.equalsIgnoreCase(SecStorageAllowedInternalDownloadSites.key()) && StringUtils.isNotEmpty(value)) { + if (SecStorageAllowedInternalDownloadSites.key().equalsIgnoreCase(name) && StringUtils.isNotEmpty(value)) { final String[] cidrs = value.split(","); for (final String cidr : cidrs) { if (!NetUtils.isValidIp4(cidr) && !NetUtils.isValidIp6(cidr) && !NetUtils.getCleanIp4Cidr(cidr).equals(cidr)) { - logger.error(String.format("Invalid CIDR %s value specified for the config %s", cidr, name)); - throw new InvalidParameterValueException(String.format("Invalid CIDR %s value specified for the config %s", cidr, name)); + return String.format("Invalid CIDR %s value specified for the config %s.", cidr, name); } } } } - if (configuration == null ) { - //range validation has to be done per case basis, for now - //return in case of Configkey parameters - return null; - } + validateIpAddressRelatedConfigValues(name, value); - if (configuration.getRange() == null) { + if (!shouldValidateConfigRange(name, value, configuration)) { return null; } - String[] range = configuration.getRange().split(","); - if (type.equals(String.class)) { - return validateIfStringValueIsInRange(name, value, range); - } else if (type.equals(Integer.class)) { + String[] range = configuration.getRange().split(","); + if (type.equals(Integer.class)) { return validateIfIntValueIsInRange(name, value, range[0]); } - return String.format("Invalid value for configuration [%s].", name); + return validateIfStringValueIsInRange(name, value, range); + } + + /** + * Returns a boolean indicating whether a Config's range should be validated. It should not be validated when:
+ *
    + *
  • The value is null;
  • + *
  • The configuration uses ConfigKey instead of Config;
  • + *
  • The Config does not have a specified range.
  • + *
+ */ + protected boolean shouldValidateConfigRange(String name, String value, Config configuration) { + if (value == null) { + logger.debug("Not proceeding with configuration [{}]'s range validation, as its provided value is null.", name); + return false; + } + + if (configuration == null) { + logger.debug("Not proceeding with configuration [{}]'s range validation, as it uses ConfigKey instead of Config.", name); + return false; + } + + if (configuration.getRange() == null) { + logger.debug("Not proceeding with configuration [{}]'s range validation, as it does not have a specified range.", name); + return false; + } + + logger.debug("Proceeding with configuration [{}]'s range validation.", name); + return true; } /** @@ -6101,7 +6125,7 @@ public NetworkOffering createNetworkOffering(final CreateNetworkOfferingCmd cmd) 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 +6133,8 @@ public NetworkOffering createNetworkOffering(final CreateNetworkOfferingCmd cmd) 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 +6166,12 @@ public NetworkOffering createNetworkOffering(final CreateNetworkOfferingCmd cmd) 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 +6233,8 @@ public NetworkOffering createNetworkOffering(final CreateNetworkOfferingCmd cmd) _networkSvc.validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(serviceOfferingId); } + NetworkOffering.RoutingMode routingMode = verifyRoutingMode(routingModeString); + // configure service provider map final Map> serviceProviderMap = new HashMap>(); final Set defaultProviders = new HashSet(); @@ -6384,6 +6404,11 @@ public NetworkOffering createNetworkOffering(final CreateNetworkOfferingCmd cmd) 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 +6445,7 @@ public NetworkOffering createNetworkOffering(final CreateNetworkOfferingCmd cmd) } 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 +6456,23 @@ public NetworkOffering createNetworkOffering(final CreateNetworkOfferingCmd cmd) 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 +6612,8 @@ public NetworkOfferingVO createNetworkOffering(final String name, final String d 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 +6644,63 @@ public NetworkOfferingVO createNetworkOffering(final String name, final String d } } + 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 +6777,14 @@ public NetworkOfferingVO createNetworkOffering(final String name, final String d } 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 +6832,17 @@ public NetworkOfferingVO createNetworkOffering(final String name, final String d 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 +6941,11 @@ public NetworkOfferingVO doInTransaction(final TransactionStatus status) { }); } - 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 +7023,7 @@ public Pair, Integer> searchForNetworkOfferings( 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 +7097,10 @@ public Pair, Integer> searchForNetworkOfferings( } } + 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); @@ -7894,7 +8002,7 @@ public ConfigKey[] getConfigKeys() { BYTES_MAX_READ_LENGTH, BYTES_MAX_WRITE_LENGTH, ADD_HOST_ON_SERVICE_RESTART_KVM, SET_HOST_DOWN_TO_MAINTENANCE, VM_SERVICE_OFFERING_MAX_CPU_CORES, VM_SERVICE_OFFERING_MAX_RAM_SIZE, MIGRATE_VM_ACROSS_CLUSTERS, ENABLE_ACCOUNT_SETTINGS_FOR_DOMAIN, ENABLE_DOMAIN_SETTINGS_FOR_CHILD_DOMAIN, - ALLOW_DOMAIN_ADMINS_TO_CREATE_TAGGED_OFFERINGS, DELETE_QUERY_BATCH_SIZE + ALLOW_DOMAIN_ADMINS_TO_CREATE_TAGGED_OFFERINGS, DELETE_QUERY_BATCH_SIZE, AllowNonRFC1918CompliantIPs }; } diff --git a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java index dc302bacae7a..19760e6d0251 100644 --- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -19,6 +19,7 @@ import static com.cloud.utils.NumbersUtil.toHumanReadableSize; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -35,6 +36,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.cpu.CPU; import com.cloud.vm.UserVmManager; import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO; import com.cloud.storage.VMTemplateVO; @@ -271,6 +273,8 @@ public void setAffinityGroupProcessors(List affinityProc _affinityProcessors = affinityProcessors; } + private static final List clusterArchTypes = Arrays.asList(CPU.CPUArch.amd64, CPU.CPUArch.arm64); + protected void avoidOtherClustersForDeploymentIfMigrationDisabled(VirtualMachine vm, Host lastHost, ExcludeList avoids) { if (lastHost == null || lastHost.getClusterId() == null || ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS.valueIn(vm.getDataCenterId())) { @@ -329,6 +333,7 @@ public DeployDestination planDeployment(VirtualMachineProfile vmProfile, Deploym logger.debug("ROOT volume [{}] {} to deploy VM [{}].", () -> getRootVolumeUuid(_volsDao.findByInstance(vm.getId())), () -> plan.getPoolId() != null ? "is ready" : "is not ready", vm::getUuid); avoidDisabledResources(vmProfile, dc, avoids); + avoidDifferentArchResources(vmProfile, dc, avoids); String haVmTag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.HaTag); String uefiFlag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.UefiFlag); @@ -455,6 +460,22 @@ public DeployDestination planDeployment(VirtualMachineProfile vmProfile, Deploym return dest; } + private void avoidDifferentArchResources(VirtualMachineProfile vmProfile, DataCenter dc, ExcludeList avoids) { + VirtualMachineTemplate template = vmProfile.getTemplate(); + for (CPU.CPUArch arch : clusterArchTypes) { + if (arch.equals(template.getArch())) { + continue; + } + List avoidClusters = _clusterDao.listClustersByArchAndZoneId(dc.getId(), arch); + if (CollectionUtils.isNotEmpty(avoidClusters)) { + logger.debug("Excluding {} clusters as they are {} arch, conflicting with the requested arch {}", + avoidClusters.size(), arch.getType(), template.getArch().getType()); + List clusterIds = avoidClusters.stream().map(x -> x.getId()).collect(Collectors.toList()); + avoids.addClusterList(clusterIds); + } + } + } + private DeployDestination deployInVmLastHost(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids, DeploymentPlanner planner, VirtualMachine vm, DataCenter dc, ServiceOffering offering, int cpuRequested, long ramRequested, boolean volumesRequireEncryption) throws InsufficientServerCapacityException { diff --git a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java index 9dbc478f9c0d..f5e053cc8308 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 Ternary, Network> doInTransaction(Transa 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 a3432a8633a8..d5b3cab44a60 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 FirewallRule createIpv6FirewallRule(CreateIpv6FirewallRuleCmd cmd) throws 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 16473e837a99..39546dc90612 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 Long makeCopyOfVpc(long vpcId, long vpcOfferingId) { 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 fb6433b5f6bb..3eeee8d5ae34 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 boolean isProviderSupportServiceInNetwork(long networkId, Service service 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 8da4185ac55a..d8d7eb20057f 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 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.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 @@ private void checkSharedNetworkCidrOverlap(Long zoneId, long physicalNetworkId, } } + 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 Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac 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 Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac 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 Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac } } + 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 Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac 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 Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac 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 Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac 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,9 +1826,13 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac 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)) { + if (Objects.nonNull(nsxProviderVO) && GuestType.L2.equals(guestType)) { throw new InvalidParameterValueException( String.format("Creation of %s networks is not supported in NSX enabled zone %s", guestType.name(), zoneName) ); @@ -1767,7 +1848,7 @@ public Network createGuestNetwork(long networkOfferingId, String name, String di 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 @@ protected void performBasicPrivateVlanChecks(String vlanId, String secondaryVlan } } - 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 Network doInTransaction(TransactionStatus status) throws InsufficientCapa } } 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 Network doInTransaction(TransactionStatus status) throws InsufficientCapa 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 @@ protected boolean canUpgrade(Network network, long oldNetworkOfferingId, long ne 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 a4d48519f43e..a87504cd07a7 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 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.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 @@ 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 @@ private static Map> setCapabilities() { 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 @@ private void updateToFailedState(Network network){ _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 0a4ef980b7bb..7194b86e3e1d 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.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 void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer } 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 @@ protected boolean revokeFirewallRule(long ruleId, boolean apply, Account caller, 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 bdabc6c03a11..4f76488337dc 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 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.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 Network design(NetworkOffering offering, DeploymentPlan plan, Network use /* 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 f8df8bef2362..c46be9bf4289 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 Network design(final NetworkOffering offering, final DeploymentPlan plan, 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 NicProfile allocate(final Network network, NicProfile nic, final VirtualM 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 ce5024a5e1b4..5c962a1cbfff 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 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.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.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 void createApplyFirewallRulesCommands(final List _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 void createFirewallRulesCommands(final List rules _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 void setupUpdateNetworkCommands(final VirtualRouter router, final Set 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 52a488fad822..8f07fcfca3b2 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.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 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V @Inject protected CommandSetupHelper _commandSetupHelper; @Inject private ManagementServer mgr; + @Inject + RoutedIpv4Manager routedIpv4Manager; + @Inject + BgpPeerDao bgpPeerDao; + @Inject + BgpPeerNetworkMapDao bgpPeerNetworkMapDao; private int _routerRamSize; private int _routerCpuMHz; @@ -2138,6 +2148,12 @@ protected StringBuilder createGuestBootLoadArgs(final NicProfile guestNic, final final StringBuilder buf = new StringBuilder(); + final NetworkOffering offering = _networkOfferingDao.findById(guestNetwork.getNetworkOfferingId()); + boolean ipv4Routed = NetworkOffering.NetworkMode.ROUTED.equals(offering.getNetworkMode()); + if (ipv4Routed) { + buf.append(" is_routed=true"); + } + boolean isIpv6Supported = _networkOfferingDao.isIpv6Supported(guestNetwork.getNetworkOfferingId()); if (isIpv6Supported) { buf.append(" ip6firewall=true"); @@ -2405,6 +2421,17 @@ protected NicProfile getControlNic(final VirtualMachineProfile profile) { return controlNic; } + protected NicProfile getGuestNic(final VirtualMachineProfile profile) { + NicProfile guestNic = null; + for (final NicProfile nic : profile.getNics()) { + if (nic.getTrafficType() == TrafficType.Guest) { + guestNic = nic; + break; + } + } + return guestNic; + } + protected void finalizeSshAndVersionAndNetworkUsageOnStart(final Commands cmds, final VirtualMachineProfile profile, final DomainRouterVO router, final NicProfile controlNic) { final DomainRouterVO vr = _routerDao.findById(profile.getId()); cmds.addCommand("checkSsh", new CheckSshCommand(profile.getInstanceName(), controlNic.getIPv4Address(), 3922)); @@ -2418,7 +2445,13 @@ protected void finalizeSshAndVersionAndNetworkUsageOnStart(final Commands cmds, // Network usage command to create iptables rules final boolean forVpc = vr.getVpcId() != null; if (!forVpc) { - cmds.addCommand("networkUsage", new NetworkUsageCommand(controlNic.getIPv4Address(), router.getHostName(), "create", forVpc)); + final NicProfile guestNic = getGuestNic(profile); + if (guestNic != null) { + Network network = _networkDao.findById(guestNic.getNetworkId()); + if (!routedIpv4Manager.isRoutedNetwork(network)) { + cmds.addCommand("networkUsage", new NetworkUsageCommand(controlNic.getIPv4Address(), router.getHostName(), "create", forVpc)); + } + } } } @@ -2447,9 +2480,13 @@ protected void finalizeNetworkRulesForNetwork(final Commands cmds, final DomainR // Fetch firewall Egress rules. if (_networkModel.isProviderSupportServiceInNetwork(guestNetworkId, Service.Firewall, provider)) { firewallRulesEgress.addAll(_rulesDao.listByNetworkPurposeTrafficType(guestNetworkId, Purpose.Firewall, FirewallRule.TrafficType.Egress)); - //create egress default rule for VR + // create egress default rule for VR createDefaultEgressFirewallRule(firewallRulesEgress, guestNetworkId); + // add routing ingress firewall rules which do not have public IPs + firewallRulesEgress.addAll(_rulesDao.listRoutingIngressFirewallRules(guestNetworkId)); + + // create egress default Ipv6 rules for VR createDefaultEgressIpv6FirewallRule(ipv6firewallRules, guestNetworkId); ipv6firewallRules.addAll(_rulesDao.listByNetworkPurposeTrafficType(guestNetworkId, Purpose.Ipv6Firewall, FirewallRule.TrafficType.Egress)); ipv6firewallRules.addAll(_rulesDao.listByNetworkPurposeTrafficType(guestNetworkId, Purpose.Ipv6Firewall, FirewallRule.TrafficType.Ingress)); @@ -2466,6 +2503,21 @@ protected void finalizeNetworkRulesForNetwork(final Commands cmds, final DomainR _commandSetupHelper.createIpv6FirewallRulesCommands(ipv6firewallRules, router, cmds, guestNetworkId); } + // Apply BGP peers + final Network guestNetwork = _networkDao.findById(guestNetworkId); + if (guestNetwork.getVpcId() != null) { + final Vpc vpc = _vpcDao.findById(guestNetwork.getVpcId()); + if (routedIpv4Manager.isDynamicRoutedVpc(vpc)) { + final List 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 @@ protected void finalizeNetworkRulesForNetwork(final Commands cmds, final DomainR createApplyLoadBalancingRulesCommands(cmds, router, provider, guestNetworkId); } // Reapply dhcp and dns configuration. - final Network guestNetwork = _networkDao.findById(guestNetworkId); if (guestNetwork.getGuestType() == GuestType.Shared && _networkModel.isProviderSupportServiceInNetwork(guestNetworkId, Service.Dhcp, provider)) { final Map dhcpCapabilities = _networkSvc.getNetworkOfferingServiceCapabilities( _networkOfferingDao.findById(_networkDao.findById(guestNetworkId).getNetworkOfferingId()), Service.Dhcp); @@ -3130,6 +3181,9 @@ public void collectNetworkStatistics(final T router, f logger.error("Could not find a network with ID => " + 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/VpcNetworkHelperImpl.java b/server/src/main/java/com/cloud/network/router/VpcNetworkHelperImpl.java index fa2f2aba8fff..13e118349b03 100644 --- a/server/src/main/java/com/cloud/network/router/VpcNetworkHelperImpl.java +++ b/server/src/main/java/com/cloud/network/router/VpcNetworkHelperImpl.java @@ -134,6 +134,7 @@ public void reallocateRouterNetworks(final RouterDeploymentDefinition vpcRouterD final PublicIp publicIp = PublicIp.createFromAddrAndVlan(ip, _vlanDao.findById(ip.getVlanId())); if ((ip.getState() == IpAddress.State.Allocated || ip.getState() == IpAddress.State.Allocating) && vpcMgr.isIpAllocatedToVpc(ip) + && Objects.nonNull(publicIp.getVlanTag()) && !publicVlans.contains(publicIp.getVlanTag())) { logger.debug("Allocating nic for router in vlan " + publicIp.getVlanTag()); final NicProfile publicNic = new NicProfile(); 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 d6c53504c681..bb517eed524d 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 boolean finalizeVirtualMachineProfile(final VirtualMachineProfile profile if (defaultIp6Dns2 != null) { buf.append(" ip6dns2=").append(defaultIp6Dns2); } + if (routedIpv4Manager.isRoutedVpc(vpc)) { + buf.append(" is_routed=true"); + } } } @@ -461,6 +464,9 @@ public boolean finalizeCommandsOnStart(final Commands cmds, final VirtualMachine 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 000000000000..a4f780c10d3f --- /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/NetworkACLServiceImpl.java b/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java index 82e8462a559d..543381732829 100644 --- a/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java @@ -697,6 +697,7 @@ public Pair, Integer> listNetworkACLItems(final L final String trafficType = cmd.getTrafficType(); final String protocol = cmd.getProtocol(); final String action = cmd.getAction(); + final String keyword = cmd.getKeyword(); final Map tags = cmd.getTags(); final Account caller = CallContext.current().getCallingAccount(); @@ -708,6 +709,7 @@ public Pair, Integer> listNetworkACLItems(final L sb.and("trafficType", sb.entity().getTrafficType(), Op.EQ); sb.and("protocol", sb.entity().getProtocol(), Op.EQ); sb.and("action", sb.entity().getAction(), Op.EQ); + sb.and("reason", sb.entity().getReason(), Op.EQ); if (tags != null && !tags.isEmpty()) { final SearchBuilder tagSearch = _resourceTagDao.createSearchBuilder(); @@ -730,6 +732,12 @@ public Pair, Integer> listNetworkACLItems(final L final SearchCriteria sc = sb.create(); + if (StringUtils.isNotBlank(keyword)) { + final SearchCriteria ssc = _networkACLItemDao.createSearchCriteria(); + ssc.addOr("protocol", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("reason", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + sc.addAnd("acl_id", SearchCriteria.Op.SC, ssc); + } if (id != null) { sc.setParameters("id", id); } @@ -747,7 +755,6 @@ public Pair, Integer> listNetworkACLItems(final L if (trafficType != null) { sc.setParameters("trafficType", trafficType); } - if (aclId != null) { // Get VPC and check access final NetworkACL acl = _networkACLDao.findById(aclId); @@ -764,7 +771,7 @@ public Pair, Integer> listNetworkACLItems(final L // aclId is not specified // List permitted VPCs and filter aclItems - final List permittedAccounts = new ArrayList(); + final List permittedAccounts = new ArrayList<>(); Long domainId = cmd.getDomainId(); boolean isRecursive = cmd.isRecursive(); final String accountName = cmd.getAccountName(); @@ -780,7 +787,7 @@ public Pair, Integer> listNetworkACLItems(final L final SearchCriteria scVpc = sbVpc.create(); _accountMgr.buildACLSearchCriteria(scVpc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); final List vpcs = _vpcDao.search(scVpc, null); - final List vpcIds = new ArrayList(); + final List vpcIds = new ArrayList<>(); for (final VpcVO vpc : vpcs) { vpcIds.add(vpc.getId()); } 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 7114bbbd90d0..d21e2bf8ca4d 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,21 @@ import javax.naming.ConfigurationException; import com.cloud.configuration.ConfigurationManager; +import com.cloud.dc.Vlan; +import com.cloud.network.dao.NsxProviderDao; +import com.cloud.network.element.NsxProviderVO; +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 +72,8 @@ 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 +191,8 @@ 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 +279,15 @@ 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 + private NsxProviderDao nsxProviderDao; + @Inject + RoutedIpv4Manager routedIpv4Manager; private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("VpcChecker")); private List vpcElements = null; @@ -328,7 +349,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { } 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 +368,8 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { 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 +390,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { } } 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 +407,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { } } 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 +425,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { } } 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 +486,18 @@ public VpcOffering createVpcOffering(CreateVPCOfferingCmd cmd) { 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 +520,31 @@ public VpcOffering createVpcOffering(CreateVPCOfferingCmd cmd) { _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); + } + + 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 nsxMode; + + 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 +586,7 @@ public VpcOffering createVpcOffering(final String name, final String displayText } } - 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 +607,11 @@ public VpcOffering createVpcOffering(final String name, final String displayText 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 +624,21 @@ public VpcOffering createVpcOffering(final String name, final String displayText // 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 +666,7 @@ public VpcOffering createVpcOffering(final String name, final String displayText @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 +678,12 @@ public VpcOfferingVO doInTransaction(final TransactionStatus status) { 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 +790,8 @@ private boolean isVpcOfferingSupportsDistributedRouter(final Map serviceCapabili 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 +1127,8 @@ public List getVpcOfferingZones(Long vpcOfferingId) { @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 +1157,21 @@ public Vpc createVpc(final long zoneId, final long vpcOffId, final long vpcOwner 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 +1213,112 @@ public Vpc createVpc(final long zoneId, final long vpcOffId, final long vpcOwner 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); + } + 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()); } - logger.info(String.format("Trying to allocate the specified IP [%s] as the source NAT of VPC [%s].", sourceNatIP, vpc)); - allocateSourceNatIp(vpc, sourceNatIP); + 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 +1354,21 @@ private String reserveSourceNatIpForNsxVpc(Account account, DataCenter zone) thr @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 +2049,8 @@ public void validateNtwkOffForVpc(final NetworkOffering guestNtwkOff, final List // 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 +2242,18 @@ public boolean cleanupVpcResources(final long vpcId, final Account caller, final _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; } @@ -3029,7 +3174,7 @@ public IpAddress associateIPToVpc(final long ipId, final long vpcId) throws Reso logger.debug("Associating ip " + ipToAssoc + " to vpc " + vpc); - final boolean isSourceNatFinal = isSrcNatIpRequired(vpc.getVpcOfferingId()) && getExistingSourceNatInVpc(vpc.getAccountId(), vpcId) == null; + final boolean isSourceNatFinal = isSrcNatIpRequired(vpc.getVpcOfferingId()) && getExistingSourceNatInVpc(vpc.getAccountId(), vpcId, false) == null; Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(final TransactionStatus status) { @@ -3091,8 +3236,9 @@ public boolean isIpAllocatedToVpc(final IpAddress ip) { @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 +3262,7 @@ public Network createVpcGuestNetwork(final long ntwkOffId, final String name, fi // 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); @@ -3125,7 +3271,7 @@ public Network createVpcGuestNetwork(final long ntwkOffId, final String name, fi return guestNetwork; } - protected IPAddressVO getExistingSourceNatInVpc(final long ownerId, final long vpcId) { + protected IPAddressVO getExistingSourceNatInVpc(final long ownerId, final long vpcId, final boolean forNsx) { final List addrs = listPublicIpsAssignedToVpc(ownerId, true, vpcId); @@ -3136,8 +3282,16 @@ protected IPAddressVO getExistingSourceNatInVpc(final long ownerId, final long v // Account already has ip addresses for (final IPAddressVO addr : addrs) { if (addr.isSourceNat()) { - sourceNatIp = addr; - return sourceNatIp; + if (!forNsx) { + sourceNatIp = addr; + } else { + if (addr.isForSystemVms()) { + sourceNatIp = addr; + } + } + if (Objects.nonNull(sourceNatIp)) { + return sourceNatIp; + } } } @@ -3161,17 +3315,23 @@ protected List listPublicIpsAssignedToVpc(final long accountId, fin } @Override - public PublicIp assignSourceNatIpAddressToVpc(final Account owner, final Vpc vpc) throws InsufficientAddressCapacityException, ConcurrentOperationException { + public PublicIp assignSourceNatIpAddressToVpc(final Account owner, final Vpc vpc, final Long podId) throws InsufficientAddressCapacityException, ConcurrentOperationException { final long dcId = vpc.getZoneId(); + NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(dcId); + boolean forNsx = nsxProvider != null; - final IPAddressVO sourceNatIp = getExistingSourceNatInVpc(owner.getId(), vpc.getId()); + final IPAddressVO sourceNatIp = getExistingSourceNatInVpc(owner.getId(), vpc.getId(), forNsx); PublicIp ipToReturn = null; if (sourceNatIp != null) { ipToReturn = PublicIp.createFromAddrAndVlan(sourceNatIp, _vlanDao.findById(sourceNatIp.getVlanId())); } else { - ipToReturn = _ipAddrMgr.assignDedicateIpAddress(owner, null, vpc.getId(), dcId, true); + if (forNsx) { + ipToReturn = _ipAddrMgr.assignPublicIpAddress(dcId, podId, owner, Vlan.VlanType.VirtualNetwork, null, null, false, true); + } else { + ipToReturn = _ipAddrMgr.assignDedicateIpAddress(owner, null, vpc.getId(), dcId, true); + } } return ipToReturn; @@ -3209,8 +3369,11 @@ public boolean applyStaticRoute(final long routeId) throws ResourceUnavailableEx @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/resource/ResourceManagerImpl.java b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java index 4fe519cfd04a..228373896204 100755 --- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java @@ -38,6 +38,7 @@ import javax.naming.ConfigurationException; import com.cloud.alert.AlertManager; +import com.cloud.cpu.CPU; import com.cloud.exception.StorageConflictException; import com.cloud.exception.StorageUnavailableException; import com.cloud.host.HostTagVO; @@ -426,6 +427,7 @@ public List discoverCluster(final AddClusterCmd cmd) throws I String url = cmd.getUrl(); final String username = cmd.getUsername(); final String password = cmd.getPassword(); + CPU.CPUArch arch = cmd.getArch(); if (url != null) { url = URLDecoder.decode(url); @@ -525,6 +527,7 @@ public List discoverCluster(final AddClusterCmd cmd) throws I cluster.setClusterType(clusterType); cluster.setAllocationState(allocationState); + cluster.setArch(arch.getType()); try { cluster = _clusterDao.persist(cluster); } catch (final Exception e) { @@ -1141,6 +1144,7 @@ public Cluster updateCluster(UpdateClusterCmd cmd) { String allocationState = cmd.getAllocationState(); String managedstate = cmd.getManagedstate(); String name = cmd.getClusterName(); + CPU.CPUArch arch = cmd.getArch(); // Verify cluster information and update the cluster if needed boolean doUpdate = false; @@ -1213,6 +1217,11 @@ public Cluster updateCluster(UpdateClusterCmd cmd) { } } + if (arch != null) { + cluster.setArch(arch.getType()); + doUpdate = true; + } + if (doUpdate) { _clusterDao.update(cluster.getId(), cluster); } @@ -2353,6 +2362,7 @@ protected HostVO createHostVO(final StartupCommand[] cmds, final ServerResource host.setLastPinged(System.currentTimeMillis() >> 10); host.setHostTags(hostTags, false); host.setDetails(details); + host.setArch(CPU.CPUArch.fromType(startup.getArch())); if (startup.getStorageIpAddressDeux() != null) { host.setStorageIpAddressDeux(startup.getStorageIpAddressDeux()); host.setStorageMacAddressDeux(startup.getStorageMacAddressDeux()); @@ -2746,6 +2756,13 @@ public HostVO fillRoutingHostVO(final HostVO host, final StartupRoutingCommand s throw new IllegalArgumentException("Can't add host whose hypervisor type is: " + hyType + " into cluster: " + clusterVO.getId() + " whose hypervisor type is: " + clusterVO.getHypervisorType()); } + CPU.CPUArch hostCpuArch = CPU.CPUArch.fromType(ssCmd.getCpuArch()); + if (hostCpuArch != null && clusterVO.getArch() != null && hostCpuArch != clusterVO.getArch()) { + String msg = String.format("Can't add a host whose arch is: %s into cluster of arch type: %s", + hostCpuArch.getType(), clusterVO.getArch().getType()); + logger.error(msg); + throw new IllegalArgumentException(msg); + } final Map hostDetails = ssCmd.getHostDetails(); if (hostDetails != null) { @@ -2764,6 +2781,7 @@ public HostVO fillRoutingHostVO(final HostVO host, final StartupRoutingCommand s host.setCaps(ssCmd.getCapabilities()); host.setCpuSockets(ssCmd.getCpuSockets()); host.setCpus(ssCmd.getCpus()); + host.setArch(hostCpuArch); host.setTotalMemory(ssCmd.getMemory()); host.setSpeed(ssCmd.getSpeed()); host.setHypervisorType(hyType); diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java index 81071db38108..7926498c1239 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 void doInTransactionWithoutResult(TransactionStatus status) { // 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 @@ private void createAndPersistDefaultNsxOffering(String name, String displayText, 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 @@ private void createAndPersistDefaultNsxOffering(String name, String displayText, } } - 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 @@ private Map getServicesAndProvidersForNSXNetwork(NetworkOffer } 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 10d5f651242a..cbde58dc7211 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.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 List> getCommands() { 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/servlet/ConsoleProxyClientParam.java b/server/src/main/java/com/cloud/servlet/ConsoleProxyClientParam.java index e23778c0b984..b416ab98288b 100644 --- a/server/src/main/java/com/cloud/servlet/ConsoleProxyClientParam.java +++ b/server/src/main/java/com/cloud/servlet/ConsoleProxyClientParam.java @@ -34,7 +34,16 @@ public class ConsoleProxyClientParam { private String username; private String password; + /** + * IP that has generated the console endpoint + */ private String sourceIP; + + /** + * IP of the client that has connected to the console + */ + private String clientIp; + private String websocketUrl; private String sessionUuid; @@ -201,4 +210,12 @@ public void setClientProvidedExtraSecurityToken(String clientProvidedExtraSecuri public void setSessionUuid(String sessionUuid) { this.sessionUuid = sessionUuid; } + + public String getClientIp() { + return clientIp; + } + + public void setClientIp(String clientIp) { + this.clientIp = clientIp; + } } diff --git a/server/src/main/java/com/cloud/storage/TemplateProfile.java b/server/src/main/java/com/cloud/storage/TemplateProfile.java index b90409480bca..49fc6836d73d 100644 --- a/server/src/main/java/com/cloud/storage/TemplateProfile.java +++ b/server/src/main/java/com/cloud/storage/TemplateProfile.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.TemplateType; @@ -28,6 +29,7 @@ public class TemplateProfile { Long userId; String name; String displayText; + CPU.CPUArch arch; Integer bits; Boolean passwordEnabled; Boolean sshKeyEnbaled; @@ -55,13 +57,14 @@ public class TemplateProfile { Boolean deployAsIs; Long size; - public TemplateProfile(Long templateId, Long userId, String name, String displayText, Integer bits, Boolean passwordEnabled, Boolean requiresHvm, String url, + public TemplateProfile(Long templateId, Long userId, String name, String displayText, CPU.CPUArch arch, Integer bits, Boolean passwordEnabled, Boolean requiresHvm, String url, Boolean isPublic, Boolean featured, Boolean isExtractable, ImageFormat format, Long guestOsId, List zoneIdList, HypervisorType hypervisorType, String accountName, Long domainId, Long accountId, String chksum, Boolean bootable, Map details, Boolean sshKeyEnabled) { this.templateId = templateId; this.userId = userId; this.name = name; this.displayText = displayText; + this.arch = arch; this.bits = bits; this.passwordEnabled = passwordEnabled; this.requiresHvm = requiresHvm; @@ -92,15 +95,16 @@ public TemplateProfile(Long userId, VMTemplateVO template, Long zoneId) { else this.zoneIdList = null; } - public TemplateProfile(Long templateId, Long userId, String name, String displayText, Integer bits, Boolean passwordEnabled, Boolean requiresHvm, String url, - Boolean isPublic, Boolean featured, Boolean isExtractable, ImageFormat format, Long guestOsId, List zoneId, + public TemplateProfile(Long templateId, Long userId, String name, String displayText, CPU.CPUArch arch, Integer bits, Boolean passwordEnabled, Boolean requiresHvm, String url, + Boolean isPublic, Boolean featured, Boolean isExtractable, ImageFormat format, Long guestOsId, List zoneId, - HypervisorType hypervisorType, String accountName, Long domainId, Long accountId, String chksum, Boolean bootable, String templateTag, Map details, - Boolean sshKeyEnabled, Long imageStoreId, Boolean isDynamicallyScalable, TemplateType templateType, Boolean directDownload, Boolean deployAsIs) { + HypervisorType hypervisorType, String accountName, Long domainId, Long accountId, String chksum, Boolean bootable, String templateTag, Map details, + Boolean sshKeyEnabled, Long imageStoreId, Boolean isDynamicallyScalable, TemplateType templateType, Boolean directDownload, Boolean deployAsIs) { this(templateId, userId, name, displayText, + arch, bits, passwordEnabled, requiresHvm, @@ -337,4 +341,8 @@ public void setSize(Long size) { public boolean isDeployAsIs() { return this.deployAsIs; } + + public CPU.CPUArch getArch() { + return arch; + } } diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index d510f688db73..1934a0ba05ee 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -126,8 +126,6 @@ import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.DiskTO; import com.cloud.api.ApiDBUtils; -import com.cloud.api.query.dao.ServiceOfferingJoinDao; -import com.cloud.api.query.vo.ServiceOfferingJoinVO; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.Resource.ResourceType; @@ -274,8 +272,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic @Inject private ServiceOfferingDetailsDao _serviceOfferingDetailsDao; @Inject - private ServiceOfferingJoinDao serviceOfferingJoinDao; - @Inject private UserVmDao _userVmDao; @Inject private UserVmDetailsDao userVmDetailsDao; @@ -1362,8 +1358,7 @@ protected boolean isNotPossibleToResize(VolumeVO volume, DiskOfferingVO diskOffe boolean isNotIso = format != null && format != ImageFormat.ISO; boolean isRoot = Volume.Type.ROOT.equals(volume.getVolumeType()); - ServiceOfferingJoinVO serviceOfferingView = serviceOfferingJoinDao.findById(diskOffering.getId()); - boolean isOfferingEnforcingRootDiskSize = serviceOfferingView != null && serviceOfferingView.getRootDiskSize() > 0; + boolean isOfferingEnforcingRootDiskSize = diskOffering.isComputeOnly() && diskOffering.getDiskSize() > 0; return isOfferingEnforcingRootDiskSize && isRoot && isNotIso; } @@ -1699,6 +1694,12 @@ public boolean stateTransitTo(Volume vol, Volume.Event event) throws NoTransitio } public void validateDestroyVolume(Volume volume, Account caller, boolean expunge, boolean forceExpunge) { + if (volume.isDeleteProtection()) { + throw new InvalidParameterValueException(String.format( + "Volume [id = %s, name = %s] has delete protection enabled and cannot be deleted.", + volume.getUuid(), volume.getName())); + } + if (expunge) { // When trying to expunge, permission is denied when the caller is not an admin and the AllowUserExpungeRecoverVolume is false for the caller. final Long userId = caller.getAccountId(); @@ -2754,13 +2755,15 @@ protected String createVolumeInfoFromVolumes(List vmVolumes) { @Override @ActionEvent(eventType = EventTypes.EVENT_VOLUME_UPDATE, eventDescription = "updating volume", async = true) - public Volume updateVolume(long volumeId, String path, String state, Long storageId, Boolean displayVolume, + public Volume updateVolume(long volumeId, String path, String state, Long storageId, + Boolean displayVolume, Boolean deleteProtection, String customId, long entityOwnerId, String chainInfo, String name) { Account caller = CallContext.current().getCallingAccount(); if (!_accountMgr.isRootAdmin(caller.getId())) { if (path != null || state != null || storageId != null || displayVolume != null || customId != null || chainInfo != null) { - throw new InvalidParameterValueException("The domain admin and normal user are not allowed to update volume except volume name"); + throw new InvalidParameterValueException("The domain admin and normal user are " + + "not allowed to update volume except volume name & delete protection"); } } @@ -2812,6 +2815,10 @@ public Volume updateVolume(long volumeId, String path, String state, Long storag volume.setName(name); } + if (deleteProtection != null) { + volume.setDeleteProtection(deleteProtection); + } + updateDisplay(volume, displayVolume); _volsDao.update(volumeId, volume); diff --git a/server/src/main/java/com/cloud/storage/upload/params/TemplateUploadParams.java b/server/src/main/java/com/cloud/storage/upload/params/TemplateUploadParams.java index 31206ca31b31..d11edce14c2a 100644 --- a/server/src/main/java/com/cloud/storage/upload/params/TemplateUploadParams.java +++ b/server/src/main/java/com/cloud/storage/upload/params/TemplateUploadParams.java @@ -16,13 +16,14 @@ // under the License. package com.cloud.storage.upload.params; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor; import java.util.Map; public class TemplateUploadParams extends UploadParamsBase { - public TemplateUploadParams(long userId, String name, String displayText, + public TemplateUploadParams(long userId, String name, String displayText, CPU.CPUArch arch, Integer bits, Boolean passwordEnabled, Boolean requiresHVM, Boolean isPublic, Boolean featured, Boolean isExtractable, String format, Long guestOSId, @@ -30,7 +31,7 @@ public TemplateUploadParams(long userId, String name, String displayText, String templateTag, long templateOwnerId, Map details, Boolean sshkeyEnabled, Boolean isDynamicallyScalable, Boolean isRoutingType, boolean deployAsIs) { - super(userId, name, displayText, bits, passwordEnabled, requiresHVM, isPublic, featured, isExtractable, + super(userId, name, displayText, arch, bits, passwordEnabled, requiresHVM, isPublic, featured, isExtractable, format, guestOSId, zoneId, hypervisorType, chksum, templateTag, templateOwnerId, details, sshkeyEnabled, isDynamicallyScalable, isRoutingType, deployAsIs); setBootable(true); diff --git a/server/src/main/java/com/cloud/storage/upload/params/UploadParams.java b/server/src/main/java/com/cloud/storage/upload/params/UploadParams.java index be8319c9e570..7be526d780d8 100644 --- a/server/src/main/java/com/cloud/storage/upload/params/UploadParams.java +++ b/server/src/main/java/com/cloud/storage/upload/params/UploadParams.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.storage.upload.params; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor; import java.util.Map; @@ -47,4 +48,5 @@ public interface UploadParams { boolean isRoutingType(); boolean isDirectDownload(); boolean isDeployAsIs(); + CPU.CPUArch getArch(); } diff --git a/server/src/main/java/com/cloud/storage/upload/params/UploadParamsBase.java b/server/src/main/java/com/cloud/storage/upload/params/UploadParamsBase.java index 60ccc27a48fc..3bf3e77fe1d4 100644 --- a/server/src/main/java/com/cloud/storage/upload/params/UploadParamsBase.java +++ b/server/src/main/java/com/cloud/storage/upload/params/UploadParamsBase.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.storage.upload.params; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor; import java.util.Map; @@ -45,8 +46,9 @@ public abstract class UploadParamsBase implements UploadParams { private boolean isDynamicallyScalable; private boolean isRoutingType; private boolean deployAsIs; + private CPU.CPUArch arch; - UploadParamsBase(long userId, String name, String displayText, + UploadParamsBase(long userId, String name, String displayText, CPU.CPUArch arch, Integer bits, boolean passwordEnabled, boolean requiresHVM, boolean isPublic, boolean featured, boolean isExtractable, String format, Long guestOSId, @@ -57,6 +59,7 @@ public abstract class UploadParamsBase implements UploadParams { this.userId = userId; this.name = name; this.displayText = displayText; + this.arch = arch; this.bits = bits; this.passwordEnabled = passwordEnabled; this.requiresHVM = requiresHVM; @@ -244,4 +247,13 @@ void setRequiresHVM(boolean requiresHVM) { void setHypervisorType(Hypervisor.HypervisorType hypervisorType) { this.hypervisorType = hypervisorType; } + + @Override + public CPU.CPUArch getArch() { + return arch; + } + + public void setArch(CPU.CPUArch arch) { + this.arch = arch; + } } diff --git a/server/src/main/java/com/cloud/template/TemplateAdapter.java b/server/src/main/java/com/cloud/template/TemplateAdapter.java index 86dd0d3cad5d..27ff563655dd 100644 --- a/server/src/main/java/com/cloud/template/TemplateAdapter.java +++ b/server/src/main/java/com/cloud/template/TemplateAdapter.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; +import com.cloud.cpu.CPU; import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd; import org.apache.cloudstack.api.command.user.iso.GetUploadParamsForIsoCmd; import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd; @@ -72,11 +73,11 @@ public String getName() { boolean delete(TemplateProfile profile); - TemplateProfile prepare(boolean isIso, Long userId, String name, String displayText, Integer bits, Boolean passwordEnabled, Boolean requiresHVM, String url, Boolean isPublic, + TemplateProfile prepare(boolean isIso, Long userId, String name, String displayText, CPU.CPUArch arch, Integer bits, Boolean passwordEnabled, Boolean requiresHVM, String url, Boolean isPublic, Boolean featured, Boolean isExtractable, String format, Long guestOSId, List zoneId, HypervisorType hypervisorType, String accountName, Long domainId, String chksum, Boolean bootable, Map details, boolean directDownload, boolean deployAsIs) throws ResourceAllocationException; - TemplateProfile prepare(boolean isIso, long userId, String name, String displayText, Integer bits, Boolean passwordEnabled, Boolean requiresHVM, String url, Boolean isPublic, + TemplateProfile prepare(boolean isIso, long userId, String name, String displayText, CPU.CPUArch arch, Integer bits, Boolean passwordEnabled, Boolean requiresHVM, String url, Boolean isPublic, Boolean featured, Boolean isExtractable, String format, Long guestOSId, List zoneId, HypervisorType hypervisorType, String chksum, Boolean bootable, String templateTag, Account templateOwner, Map details, Boolean sshKeyEnabled, String imageStoreUuid, Boolean isDynamicallyScalable, TemplateType templateType, boolean directDownload, boolean deployAsIs) throws ResourceAllocationException; diff --git a/server/src/main/java/com/cloud/template/TemplateAdapterBase.java b/server/src/main/java/com/cloud/template/TemplateAdapterBase.java index d663a9ae0b71..119589dcc65f 100644 --- a/server/src/main/java/com/cloud/template/TemplateAdapterBase.java +++ b/server/src/main/java/com/cloud/template/TemplateAdapterBase.java @@ -24,6 +24,7 @@ import javax.inject.Inject; +import com.cloud.cpu.CPU; import com.cloud.deployasis.DeployAsIsConstants; import com.cloud.storage.upload.params.IsoUploadParams; import com.cloud.storage.upload.params.TemplateUploadParams; @@ -130,18 +131,18 @@ public boolean stop() { } @Override - public TemplateProfile prepare(boolean isIso, Long userId, String name, String displayText, Integer bits, Boolean passwordEnabled, Boolean requiresHVM, String url, - Boolean isPublic, Boolean featured, Boolean isExtractable, String format, Long guestOSId, List zoneId, HypervisorType hypervisorType, String accountName, - Long domainId, String chksum, Boolean bootable, Map details, boolean directDownload, boolean deployAsIs) throws ResourceAllocationException { - return prepare(isIso, userId, name, displayText, bits, passwordEnabled, requiresHVM, url, isPublic, featured, isExtractable, format, guestOSId, zoneId, + public TemplateProfile prepare(boolean isIso, Long userId, String name, String displayText, CPU.CPUArch arch, Integer bits, Boolean passwordEnabled, Boolean requiresHVM, String url, + Boolean isPublic, Boolean featured, Boolean isExtractable, String format, Long guestOSId, List zoneId, HypervisorType hypervisorType, String accountName, + Long domainId, String chksum, Boolean bootable, Map details, boolean directDownload, boolean deployAsIs) throws ResourceAllocationException { + return prepare(isIso, userId, name, displayText, arch, bits, passwordEnabled, requiresHVM, url, isPublic, featured, isExtractable, format, guestOSId, zoneId, hypervisorType, chksum, bootable, null, null, details, false, null, false, TemplateType.USER, directDownload, deployAsIs); } @Override - public TemplateProfile prepare(boolean isIso, long userId, String name, String displayText, Integer bits, Boolean passwordEnabled, Boolean requiresHVM, String url, - Boolean isPublic, Boolean featured, Boolean isExtractable, String format, Long guestOSId, List zoneIdList, HypervisorType hypervisorType, String chksum, - Boolean bootable, String templateTag, Account templateOwner, Map details, Boolean sshkeyEnabled, String imageStoreUuid, Boolean isDynamicallyScalable, - TemplateType templateType, boolean directDownload, boolean deployAsIs) throws ResourceAllocationException { + public TemplateProfile prepare(boolean isIso, long userId, String name, String displayText, CPU.CPUArch arch, Integer bits, Boolean passwordEnabled, Boolean requiresHVM, String url, + Boolean isPublic, Boolean featured, Boolean isExtractable, String format, Long guestOSId, List zoneIdList, HypervisorType hypervisorType, String chksum, + Boolean bootable, String templateTag, Account templateOwner, Map details, Boolean sshkeyEnabled, String imageStoreUuid, Boolean isDynamicallyScalable, + TemplateType templateType, boolean directDownload, boolean deployAsIs) throws ResourceAllocationException { //Long accountId = null; // parameters verification @@ -262,7 +263,7 @@ public TemplateProfile prepare(boolean isIso, long userId, String name, String d Long id = _tmpltDao.getNextInSequence(Long.class, "id"); CallContext.current().setEventDetails("Id: " + id + " name: " + name); - return new TemplateProfile(id, userId, name, displayText, bits, passwordEnabled, requiresHVM, url, isPublic, featured, isExtractable, imgfmt, guestOSId, zoneIdList, + return new TemplateProfile(id, userId, name, displayText, arch, bits, passwordEnabled, requiresHVM, url, isPublic, featured, isExtractable, imgfmt, guestOSId, zoneIdList, hypervisorType, templateOwner.getAccountName(), templateOwner.getDomainId(), templateOwner.getAccountId(), chksum, bootable, templateTag, details, sshkeyEnabled, null, isDynamicallyScalable, templateType, directDownload, deployAsIs); @@ -306,7 +307,7 @@ public TemplateProfile prepare(RegisterTemplateCmd cmd) throws ResourceAllocatio } } } - return prepare(false, CallContext.current().getCallingUserId(), cmd.getTemplateName(), cmd.getDisplayText(), cmd.getBits(), cmd.isPasswordEnabled(), cmd.getRequiresHvm(), + return prepare(false, CallContext.current().getCallingUserId(), cmd.getTemplateName(), cmd.getDisplayText(), cmd.getArch(), cmd.getBits(), cmd.isPasswordEnabled(), cmd.getRequiresHvm(), cmd.getUrl(), cmd.isPublic(), cmd.isFeatured(), cmd.isExtractable(), cmd.getFormat(), cmd.getOsTypeId(), zoneId, hypervisorType, cmd.getChecksum(), true, cmd.getTemplateTag(), owner, details, cmd.isSshKeyEnabled(), null, cmd.isDynamicallyScalable(), templateType, cmd.isDirectDownload(), cmd.isDeployAsIs()); @@ -337,7 +338,7 @@ private TemplateProfile prepareUploadParamsInternal(UploadParams params) throws StringUtils.join(Arrays.stream(HypervisorType.values()).filter(h -> h != HypervisorType.None).map(HypervisorType::name).toArray(), ", "))); } - return prepare(params.isIso(), params.getUserId(), params.getName(), params.getDisplayText(), params.getBits(), + return prepare(params.isIso(), params.getUserId(), params.getName(), params.getDisplayText(), params.getArch(), params.getBits(), params.isPasswordEnabled(), params.requiresHVM(), params.getUrl(), params.isPublic(), params.isFeatured(), params.isExtractable(), params.getFormat(), params.getGuestOSId(), zoneList, params.getHypervisorType(), params.getChecksum(), params.isBootable(), params.getTemplateTag(), owner, @@ -358,7 +359,7 @@ public TemplateProfile prepare(GetUploadParamsForTemplateCmd cmd) throws Resourc osTypeId = getDefaultDeployAsIsGuestOsId(); } UploadParams params = new TemplateUploadParams(CallContext.current().getCallingUserId(), cmd.getName(), - cmd.getDisplayText(), cmd.getBits(), BooleanUtils.toBoolean(cmd.isPasswordEnabled()), + cmd.getDisplayText(), cmd.getArch(), cmd.getBits(), BooleanUtils.toBoolean(cmd.isPasswordEnabled()), BooleanUtils.toBoolean(cmd.getRequiresHvm()), BooleanUtils.toBoolean(cmd.isPublic()), BooleanUtils.toBoolean(cmd.isFeatured()), BooleanUtils.toBoolean(cmd.isExtractable()), cmd.getFormat(), osTypeId, cmd.getZoneId(), HypervisorType.getType(cmd.getHypervisor()), cmd.getChecksum(), @@ -392,7 +393,7 @@ public TemplateProfile prepare(RegisterIsoCmd cmd) throws ResourceAllocationExce zoneList.add(zoneId); } - return prepare(true, CallContext.current().getCallingUserId(), cmd.getIsoName(), cmd.getDisplayText(), 64, cmd.isPasswordEnabled(), true, cmd.getUrl(), cmd.isPublic(), + return prepare(true, CallContext.current().getCallingUserId(), cmd.getIsoName(), cmd.getDisplayText(), cmd.getArch(), 64, cmd.isPasswordEnabled(), true, cmd.getUrl(), cmd.isPublic(), cmd.isFeatured(), cmd.isExtractable(), ImageFormat.ISO.toString(), cmd.getOsTypeId(), zoneList, HypervisorType.None, cmd.getChecksum(), cmd.isBootable(), null, owner, null, false, cmd.getImageStoreUuid(), cmd.isDynamicallyScalable(), TemplateType.USER, cmd.isDirectDownload(), false); } @@ -403,7 +404,7 @@ protected VMTemplateVO persistTemplate(TemplateProfile profile, VirtualMachineTe new VMTemplateVO(profile.getTemplateId(), profile.getName(), profile.getFormat(), profile.isPublic(), profile.isFeatured(), profile.isExtractable(), profile.getTemplateType(), profile.getUrl(), profile.isRequiresHVM(), profile.getBits(), profile.getAccountId(), profile.getCheckSum(), profile.getDisplayText(), profile.isPasswordEnabled(), profile.getGuestOsId(), profile.isBootable(), profile.getHypervisorType(), - profile.getTemplateTag(), profile.getDetails(), profile.isSshKeyEnabled(), profile.IsDynamicallyScalable(), profile.isDirectDownload(), profile.isDeployAsIs()); + profile.getTemplateTag(), profile.getDetails(), profile.isSshKeyEnabled(), profile.IsDynamicallyScalable(), profile.isDirectDownload(), profile.isDeployAsIs(), profile.getArch()); template.setState(initialState); if (profile.isDirectDownload()) { diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java index 8752abbca34c..f58e5bf9aabd 100755 --- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java @@ -34,6 +34,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.cpu.CPU; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; @@ -1918,9 +1919,13 @@ public VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd, Account t String description = cmd.getDisplayText(); boolean isExtractable = false; Long sourceTemplateId = null; + CPU.CPUArch arch = CPU.CPUArch.amd64; if (volume != null) { VMTemplateVO template = ApiDBUtils.findTemplateById(volume.getTemplateId()); isExtractable = template != null && template.isExtractable() && template.getTemplateType() != Storage.TemplateType.SYSTEM; + if (template != null) { + arch = template.getArch(); + } if (volume.getIsoId() != null && volume.getIsoId() != 0) { sourceTemplateId = volume.getIsoId(); } else if (volume.getTemplateId() != null) { @@ -1935,7 +1940,7 @@ public VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd, Account t } privateTemplate = new VMTemplateVO(nextTemplateId, name, ImageFormat.RAW, isPublic, featured, isExtractable, TemplateType.USER, null, requiresHvmValue, bitsValue, templateOwner.getId(), null, description, - passwordEnabledValue, guestOS.getId(), true, hyperType, templateTag, cmd.getDetails(), sshKeyEnabledValue, isDynamicScalingEnabled, false, false); + passwordEnabledValue, guestOS.getId(), true, hyperType, templateTag, cmd.getDetails(), sshKeyEnabledValue, isDynamicScalingEnabled, false, false, arch); if (sourceTemplateId != null) { if (logger.isDebugEnabled()) { @@ -2113,6 +2118,7 @@ private VMTemplateVO updateTemplateOrIso(BaseUpdateTemplateOrIsoCmd cmd) { Map details = cmd.getDetails(); Account account = CallContext.current().getCallingAccount(); boolean cleanupDetails = cmd.isCleanupDetails(); + CPU.CPUArch arch = cmd.getCPUArch(); // verify that template exists VMTemplateVO template = _tmpltDao.findById(id); @@ -2161,6 +2167,7 @@ private VMTemplateVO updateTemplateOrIso(BaseUpdateTemplateOrIsoCmd cmd) { isRoutingTemplate == null && templateType == null && templateTag == null && + arch == null && (! cleanupDetails && details == null) //update details in every case except this one ); if (!updateNeeded) { @@ -2235,6 +2242,10 @@ private VMTemplateVO updateTemplateOrIso(BaseUpdateTemplateOrIsoCmd cmd) { template.setDynamicallyScalable(isDynamicallyScalable); } + if (arch != null) { + template.setArch(arch); + } + if (isRoutingTemplate != null) { if (isRoutingTemplate) { template.setTemplateType(TemplateType.ROUTING); diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index 07d06fbd2f7e..6a9e15a58c70 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.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 int compare(NetworkVO network1, NetworkVO network2) { } } + // 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 51705e63f3a9..4a81772d6d75 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.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 @@ private boolean cleanDomain(DomainVO domain, Boolean cleanup) { 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/UserVmManager.java b/server/src/main/java/com/cloud/vm/UserVmManager.java index 31b0bc405973..f2a8a672d42a 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManager.java +++ b/server/src/main/java/com/cloud/vm/UserVmManager.java @@ -141,8 +141,14 @@ boolean upgradeVirtualMachine(Long id, Long serviceOfferingId, Map securityGroupIdList, Map> extraDhcpOptionsMap) throws ResourceUnavailableException, InsufficientCapacityException; + UserVm updateVirtualMachine(long id, String displayName, String group, Boolean ha, + Boolean isDisplayVmEnabled, Boolean deleteProtection, + Long osTypeId, String userData, Long userDataId, + String userDataDetails, Boolean isDynamicallyScalable, + HTTPMethod httpMethod, String customId, String hostName, + String instanceName, List securityGroupIdList, + Map> extraDhcpOptionsMap + ) throws ResourceUnavailableException, InsufficientCapacityException; //the validateCustomParameters, save and remove CustomOfferingDetils functions can be removed from the interface once we can //find a common place for all the scaling and upgrading code of both user and systemvms. diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 75dd6911a9e3..14eea2083d26 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -2937,8 +2937,11 @@ public UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableEx } } } - return updateVirtualMachine(id, displayName, group, ha, isDisplayVm, osTypeId, userData, userDataId, userDataDetails, isDynamicallyScalable, - cmd.getHttpMethod(), cmd.getCustomId(), hostName, cmd.getInstanceName(), securityGroupIdList, cmd.getDhcpOptionsMap()); + return updateVirtualMachine(id, displayName, group, ha, isDisplayVm, + cmd.getDeleteProtection(), osTypeId, userData, + userDataId, userDataDetails, isDynamicallyScalable, cmd.getHttpMethod(), + cmd.getCustomId(), hostName, cmd.getInstanceName(), securityGroupIdList, + cmd.getDhcpOptionsMap()); } private boolean isExtraConfig(String detailName) { @@ -3039,9 +3042,14 @@ private void generateNetworkUsageForVm(VirtualMachine vm, boolean isDisplay, Str } @Override - public UserVm updateVirtualMachine(long id, String displayName, String group, Boolean ha, Boolean isDisplayVmEnabled, Long osTypeId, String userData, - Long userDataId, String userDataDetails, Boolean isDynamicallyScalable, HTTPMethod httpMethod, String customId, String hostName, String instanceName, List securityGroupIdList, Map> extraDhcpOptionsMap) - throws ResourceUnavailableException, InsufficientCapacityException { + public UserVm updateVirtualMachine(long id, String displayName, String group, Boolean ha, + Boolean isDisplayVmEnabled, Boolean deleteProtection, + Long osTypeId, String userData, Long userDataId, + String userDataDetails, Boolean isDynamicallyScalable, + HTTPMethod httpMethod, String customId, String hostName, + String instanceName, List securityGroupIdList, + Map> extraDhcpOptionsMap + ) throws ResourceUnavailableException, InsufficientCapacityException { UserVmVO vm = _vmDao.findById(id); if (vm == null) { throw new CloudRuntimeException("Unable to find virtual machine with id " + id); @@ -3076,6 +3084,10 @@ public UserVm updateVirtualMachine(long id, String displayName, String group, Bo isDisplayVmEnabled = vm.isDisplayVm(); } + if (deleteProtection == null) { + deleteProtection = vm.isDeleteProtection(); + } + boolean updateUserdata = false; if (userData != null) { // check and replace newlines @@ -3190,7 +3202,9 @@ public UserVm updateVirtualMachine(long id, String displayName, String group, Bo .getUuid(), nic.getId(), extraDhcpOptionsMap); } - _vmDao.updateVM(id, displayName, ha, osTypeId, userData, userDataId, userDataDetails, isDisplayVmEnabled, isDynamicallyScalable, customId, hostName, instanceName); + _vmDao.updateVM(id, displayName, ha, osTypeId, userData, userDataId, + userDataDetails, isDisplayVmEnabled, isDynamicallyScalable, + deleteProtection, customId, hostName, instanceName); if (updateUserdata) { updateUserData(vm); @@ -3427,6 +3441,12 @@ public UserVm destroyVm(DestroyVMCmd cmd) throws ResourceUnavailableException, C return vm; } + if (vm.isDeleteProtection()) { + throw new InvalidParameterValueException(String.format( + "Instance [id = %s, name = %s] has delete protection enabled and cannot be deleted.", + vm.getUuid(), vm.getName())); + } + // check if vm belongs to AutoScale vm group in Disabled state autoScaleManager.checkIfVmActionAllowed(vmId); @@ -4007,7 +4027,7 @@ private NetworkVO createDefaultNetworkForAccount(DataCenter zone, Account owner, 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()); } @@ -7835,7 +7855,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) { 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); @@ -8611,6 +8631,11 @@ private void validateVolumes(List volumes) { if (!(volume.getVolumeType() == Volume.Type.ROOT || volume.getVolumeType() == Volume.Type.DATADISK)) { throw new InvalidParameterValueException("Please specify volume of type " + Volume.Type.DATADISK.toString() + " or " + Volume.Type.ROOT.toString()); } + if (volume.isDeleteProtection()) { + throw new InvalidParameterValueException(String.format( + "Volume [id = %s, name = %s] has delete protection enabled and cannot be deleted", + volume.getUuid(), volume.getName())); + } } } 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 000000000000..bbad93737f13 --- /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 2dea5a4223fa..817cfe07e58e 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 @@ protected boolean prepareDeployment() { 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/router/deployment/VpcRouterDeploymentDefinition.java b/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java index aa44f29efcdb..405575c65b11 100644 --- a/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java +++ b/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java @@ -22,7 +22,6 @@ import java.util.Objects; import com.cloud.dc.DataCenter; -import com.cloud.dc.Vlan; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.element.NsxProviderVO; @@ -132,10 +131,9 @@ protected void findSourceNatIP() throws InsufficientAddressCapacityException, Co if (isPublicNetwork) { if (Objects.isNull(nsxProvider)) { - sourceNatIp = vpcMgr.assignSourceNatIpAddressToVpc(owner, vpc); + sourceNatIp = vpcMgr.assignSourceNatIpAddressToVpc(owner, vpc, null); } else { - // NSX deploys VRs with Public NIC != to the source NAT, the source NAT IP is on the NSX Public range - sourceNatIp = ipAddrMgr.assignPublicIpAddress(zoneId, getPodId(), owner, Vlan.VlanType.VirtualNetwork, null, null, false, true); + sourceNatIp = vpcMgr.assignSourceNatIpAddressToVpc(owner, vpc, getPodId()); if (vpc != null) { IPAddressVO routerPublicIp = ipAddressDao.findByIp(sourceNatIp.getAddress().toString()); routerPublicIp.setVpcId(vpc.getId()); 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 e777e959b849..5c1fc5e9ac6e 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.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.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 boolean applyNetworkACLs(final Network network, final List(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 4db46ac36bb6..bc2271b01632 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.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 boolean visit(final AdvancedVpnRules vpnRules) throws ResourceUnavailable // 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 77519c500b23..65d702b71380 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 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 boolean removeDhcpEntry(Network network, NicProfile nic, VirtualMachinePr 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 78f281f32cfe..8702a58ad69e 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 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 boolean visit(final StaticRoutesRules staticRoutesRules) throws ResourceU 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 aff40ce69acb..176584780fed 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.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 @@ boolean applyRules(final Network network, final VirtualRouter router, final Stri 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 035c67457e5d..07bbcc191602 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 @@ 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 12584e296f21..2504cabf8091 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 @@ private NetworkVO createDefaultNetworkForAccount(DataCenter zone, Account owner, 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 3237d11d1c7a..e9b1cad78d77 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 000000000000..ace7bc856918 --- /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 a4ffa04a411e..0a0452968213 100644 --- a/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java +++ b/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java @@ -59,9 +59,8 @@ import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.impl.ConfigurationVO; -import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; -import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; +import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; import org.apache.cloudstack.vm.UnmanagedVMsManager; import org.junit.Assert; import org.junit.Before; @@ -82,8 +81,6 @@ 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; @@ -96,10 +93,12 @@ @RunWith(MockitoJUnitRunner.class) public class ConfigurationManagerImplTest { + + @InjectMocks + @Spy + ConfigurationManagerImpl configurationManagerImplSpy; @Mock ConfigDepot configDepot; - @InjectMocks - ConfigurationManagerImpl configurationManagerImplSpy = Mockito.spy(new ConfigurationManagerImpl()); @Mock SearchCriteria searchCriteriaDiskOfferingDetailMock; @Mock @@ -111,18 +110,17 @@ public class ConfigurationManagerImplTest { @Mock Domain domainMock; @Mock - DataCenterDao zoneDaoMock; - @Mock DomainDao domainDaoMock; @Mock EntityManager entityManagerMock; @Mock - DiskOfferingDetailsDao diskOfferingDetailsDao; + ConfigurationVO configurationVOMock; + @Mock + ConfigKey configKeyMock; @Spy DiskOfferingVO diskOfferingVOSpy; @Mock UpdateDiskOfferingCmd updateDiskOfferingCmdMock; - @Mock NsxProviderDao nsxProviderDao; @Mock @@ -177,24 +175,13 @@ public class ConfigurationManagerImplTest { @Before public void setUp() throws Exception { - configurationManagerImplSpy._configDepot = configDepot; - configurationManagerImplSpy.nsxProviderDao = nsxProviderDao; - configurationManagerImplSpy._zoneDao = zoneDao; - configurationManagerImplSpy._hostDao = hostDao; - configurationManagerImplSpy._podDao = podDao; - configurationManagerImplSpy._privateIpAddressDao = ipAddressDao; - configurationManagerImplSpy._publicIpAddressDao = publicIpAddressDao; - configurationManagerImplSpy._vmInstanceDao = vmInstanceDao; - configurationManagerImplSpy._volumeDao = volumeDao; - configurationManagerImplSpy._physicalNetworkDao = physicalNetworkDao; - configurationManagerImplSpy._imageStoreDao = imageStoreDao; - configurationManagerImplSpy._vlanDao = vlanDao; - configurationManagerImplSpy._capacityDao = capacityDao; - configurationManagerImplSpy._dedicatedDao = dedicatedResourceDao; - configurationManagerImplSpy._configDao = configDao; - configurationManagerImplSpy._networkOfferingDao = networkOfferingDao; - configurationManagerImplSpy._networkSvc = networkService; - configurationManagerImplSpy._networkModel = networkModel; + Mockito.when(configurationVOMock.getScope()).thenReturn(ConfigKey.Scope.Global.name()); + Mockito.when(configDao.findByName(Mockito.anyString())).thenReturn(configurationVOMock); + Mockito.when(configDepot.get(Mockito.anyString())).thenReturn(configKeyMock); + + configurationManagerImplSpy.populateConfigValuesForValidationSet(); + configurationManagerImplSpy.weightBasedParametersForValidation(); + configurationManagerImplSpy.overProvisioningFactorsForValidation(); ReflectionTestUtils.setField(configurationManagerImplSpy, "templateZoneDao", vmTemplateZoneDao); ReflectionTestUtils.setField(configurationManagerImplSpy, "annotationDao", annotationDao); @@ -208,7 +195,6 @@ public void validateIfIntValueIsInRangeTestValidValueReturnNull() { Assert.assertNull(testVariable); } - @Test public void validateIfIntValueIsInRangeTestInvalidValueReturnString() { String testVariable = configurationManagerImplSpy.validateIfIntValueIsInRange("String name", "9", "1-5"); @@ -442,17 +428,12 @@ public void testCreateNetworkOfferingForNsx() { 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()); @@ -476,14 +457,17 @@ public void testValidateInvalidScopeForConfiguration() { Assert.assertEquals("Invalid scope id provided for the parameter test.config.name", msg); } - @Test(expected = InvalidParameterValueException.class) + @Test public void testValidateConfig_ThreadsOnKVMHostToTransferVMwareVMFiles_Failure() { ConfigurationVO cfg = mock(ConfigurationVO.class); when(cfg.getScope()).thenReturn(ConfigKey.Scope.Global.toString()); ConfigKey configKey = UnmanagedVMsManager.ThreadsOnKVMHostToImportVMwareVMFiles; Mockito.doReturn(cfg).when(configDao).findByName(Mockito.anyString()); Mockito.doReturn(configKey).when(configurationManagerImplSpy._configDepot).get(configKey.key()); - configurationManagerImplSpy.validateConfigurationValue(configKey.key(), "11", configKey.scope().toString()); + + String result = configurationManagerImplSpy.validateConfigurationValue(configKey.key(), "11", configKey.scope().toString()); + + Assert.assertNotNull(result); } @Test @@ -497,7 +481,7 @@ public void testValidateConfig_ThreadsOnKVMHostToTransferVMwareVMFiles_Success() Assert.assertNull(msg); } - @Test(expected = InvalidParameterValueException.class) + @Test public void testValidateConfig_ConvertVmwareInstanceToKvmTimeout_Failure() { ConfigurationVO cfg = mock(ConfigurationVO.class); when(cfg.getScope()).thenReturn(ConfigKey.Scope.Global.toString()); @@ -505,7 +489,10 @@ public void testValidateConfig_ConvertVmwareInstanceToKvmTimeout_Failure() { Mockito.doReturn(cfg).when(configDao).findByName(Mockito.anyString()); Mockito.doReturn(configKey).when(configurationManagerImplSpy._configDepot).get(configKey.key()); configurationManagerImplSpy.populateConfigValuesForValidationSet(); - configurationManagerImplSpy.validateConfigurationValue(configKey.key(), "0", configKey.scope().toString()); + + String result = configurationManagerImplSpy.validateConfigurationValue(configKey.key(), "0", configKey.scope().toString()); + + Assert.assertNotNull(result); } @Test @@ -627,4 +614,241 @@ public void checkIfDomainIsChildDomainTestNonChildDomainThrowException() { Assert.assertThrows(InvalidParameterValueException.class, () -> configurationManagerImplSpy.checkIfDomainIsChildDomain(diskOfferingMock, accountMock, userMock, filteredDomainIds)); } + + @Test + public void validateConfigurationValueTestValidatesValueType() { + Mockito.when(configKeyMock.type()).thenReturn(Integer.class); + configurationManagerImplSpy.validateConfigurationValue("validate.type", "100", ConfigKey.Scope.Global.name()); + Mockito.verify(configurationManagerImplSpy).validateValueType("100", Integer.class); + } + + @Test + public void validateConfigurationValueTestValidatesValueRange() { + Mockito.when(configKeyMock.type()).thenReturn(Integer.class); + configurationManagerImplSpy.validateConfigurationValue("validate.range", "100", ConfigKey.Scope.Global.name()); + Mockito.verify(configurationManagerImplSpy).validateValueRange("validate.range", "100", Integer.class, null); + } + + @Test + public void validateValueTypeTestReturnsTrueWhenValueIsNullAndTypeIsString() { + Assert.assertTrue(configurationManagerImplSpy.validateValueType(null, String.class)); + } + + @Test + public void validateValueTypeTestReturnsTrueWhenValueIsNumericAndTypeIsString() { + Assert.assertTrue(configurationManagerImplSpy.validateValueType("1", String.class)); + } + + @Test + public void validateValueTypeTestReturnsTrueWhenValueIsStringAndTypeIsString() { + Assert.assertTrue(configurationManagerImplSpy.validateValueType("test", String.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsNullAndTypeIsBoolean() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType(null, Boolean.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsNumericAndTypeIsBoolean() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("1", Boolean.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsStringAndTypeIsBoolean() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("test", Boolean.class)); + } + + @Test + public void validateValueTypeTestReturnsTrueWhenValueIsTrueAndTypeIsBoolean() { + Assert.assertTrue(configurationManagerImplSpy.validateValueType("true", Boolean.class)); + + } + + @Test + public void validateValueTypeTestReturnsTrueWhenValueIsFalseAndTypeIsBoolean() { + Assert.assertTrue(configurationManagerImplSpy.validateValueType("false", Boolean.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsNullAndTypeIsInteger() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType(null, Integer.class)); + } + + @Test + public void validateValueTypeTestReturnsTrueWhenValueIsIntegerAndTypeIsInteger() { + Assert.assertTrue(configurationManagerImplSpy.validateValueType("-2147483647", Integer.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueExceedsIntegerLimitAndTypeIsInteger() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("2147483648", Integer.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsDecimalAndTypeIsInteger() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("1.1", Integer.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsStringAndTypeIsInteger() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("test", Integer.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsNullAndTypeIsShort() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType(null, Short.class)); + } + + @Test + public void validateValueTypeTestReturnsTrueWhenValueIsIntegerAndTypeIsShort() { + Assert.assertTrue(configurationManagerImplSpy.validateValueType("-32768", Short.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueExceedsShortLimitAndTypeIsShort() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("32768", Short.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsDecimalAndTypeIsShort() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("1.1", Short.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsStringAndTypeIsShort() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("test", Short.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsNullAndTypeIsLong() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType(null, Long.class)); + } + + @Test + public void validateValueTypeTestReturnsTrueWhenValueIsIntegerAndTypeIsLong() { + Assert.assertTrue(configurationManagerImplSpy.validateValueType("-9223372036854775807", Long.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueExceedsLongLimitAndTypeIsLong() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("9223372036854775808", Long.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsDecimalAndTypeIsLong() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("1.1", Long.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsStringAndTypeIsLong() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("test", Long.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsNullAndTypeIsFloat() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType(null, Float.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsInfiniteAndTypeIsFloat() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", Float.class)); + } + + @Test + public void validateValueTypeTestReturnsTrueWhenValueIsNumericAndTypeIsFloat() { + Assert.assertTrue(configurationManagerImplSpy.validateValueType("1.1", Float.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsStringAndTypeIsFloat() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("test", Float.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsNullAndTypeIsDouble() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType(null, Double.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsInfiniteAndTypeIsDouble() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", Double.class)); + } + + @Test + public void validateValueTypeTestReturnsTrueWhenValueIsNumericAndTypeIsDouble() { + Assert.assertTrue(configurationManagerImplSpy.validateValueType("1.1", Double.class)); + } + + @Test + public void validateValueTypeTestReturnsFalseWhenValueIsStringAndTypeIsDouble() { + Assert.assertFalse(configurationManagerImplSpy.validateValueType("test", Double.class)); + } + + @Test + public void validateValueRangeTestReturnsNullWhenConfigKeyHasNoRange() { + Assert.assertNull(configurationManagerImplSpy.validateValueRange("configkey.without.range", "0", Integer.class, null)); + } + + @Test + public void validateValueRangeTestReturnsNullWhenConfigKeyHasRangeAndValueIsValid() { + Assert.assertNull(configurationManagerImplSpy.validateValueRange(NetworkModel.MACIdentifier.key(), "100", Integer.class, null)); + } + + @Test + public void validateValueRangeTestReturnsNotNullWhenConfigKeyHasRangeAndValueIsInvalid() { + Assert.assertNotNull(configurationManagerImplSpy.validateValueRange(NetworkModel.MACIdentifier.key(), "-1", Integer.class, null)); + } + + @Test + public void validateValueRangeTestValidatesValueWhenConfigHasRange() { + Config config = Config.SecStorageEncryptCopy; + String name = config.name(); + String value = "value"; + String expectedResult = "expectedResult"; + + Mockito.doReturn(expectedResult).when(configurationManagerImplSpy).validateIfStringValueIsInRange(name, value, config.getRange().split(",")); + + String result = configurationManagerImplSpy.validateValueRange(name, value, config.getType(), config); + + Assert.assertEquals(expectedResult, result); + } + + @Test + public void validateValueRangeTestValidatesIntValueWhenConfigHasNumericRange() { + Config config = Config.RouterExtraPublicNics; + String name = config.name(); + String value = "1"; + String expectedResult = "expectedResult"; + + Mockito.doReturn(expectedResult).when(configurationManagerImplSpy).validateIfIntValueIsInRange(name, value, config.getRange()); + + String result = configurationManagerImplSpy.validateValueRange(name, value, config.getType(), config); + + Assert.assertEquals(expectedResult, result); + } + + @Test + public void shouldValidateConfigRangeTestValueIsNullReturnFalse() { + boolean result = configurationManagerImplSpy.shouldValidateConfigRange(Config.ConsoleProxyUrlDomain.name(), null, Config.ConsoleProxyUrlDomain); + Assert.assertFalse(result); + } + + @Test + public void shouldValidateConfigRangeTestConfigIsNullReturnFalse() { + boolean result = configurationManagerImplSpy.shouldValidateConfigRange("", "test", null); + Assert.assertFalse(result); + } + + @Test + public void shouldValidateConfigRangeTestConfigDoesNotHaveARangeReturnFalse() { + boolean result = configurationManagerImplSpy.shouldValidateConfigRange(Config.ConsoleProxySessionMax.name(), "test", Config.ConsoleProxySessionMax); + Assert.assertFalse(result); + } + + @Test + public void shouldValidateConfigRangeTestValueIsNotNullAndConfigHasRangeReturnTrue() { + boolean result = configurationManagerImplSpy.shouldValidateConfigRange(Config.ConsoleProxySessionMax.name(), "test", Config.ConsoleProxyUrlDomain); + Assert.assertTrue(result); + } } diff --git a/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java b/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java index 312719eb8509..4ae871e1ba58 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.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 void validateInvalidStaticNatServiceCapablitiesTest() { @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/deploy/DeploymentPlanningManagerImplTest.java b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java index 3afd3dc4a958..482d17908f43 100644 --- a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java +++ b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java @@ -21,6 +21,7 @@ import com.cloud.capacity.CapacityManager; import com.cloud.capacity.dao.CapacityDao; import com.cloud.configuration.ConfigurationManagerImpl; +import com.cloud.cpu.CPU; import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.ClusterVO; @@ -295,6 +296,10 @@ public void dataCenterAvoidTest() throws InsufficientServerCapacityException, Af DataCenterDeployment plan = new DataCenterDeployment(dataCenterId); Mockito.when(avoids.shouldAvoid((DataCenterVO) ArgumentMatchers.any())).thenReturn(true); + VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class); + Mockito.when(template.getArch()).thenReturn(CPU.CPUArch.amd64); + Mockito.when(vmProfile.getTemplate()).thenReturn(template); + Mockito.when(_clusterDao.listClustersByArchAndZoneId(dataCenterId, CPU.CPUArch.arm64)).thenReturn(null); DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, null); assertNull("DataCenter is in avoid set, destination should be null! ", dest); } @@ -310,6 +315,10 @@ public void plannerCannotHandleTest() throws InsufficientServerCapacityException Mockito.when(avoids.shouldAvoid((DataCenterVO) ArgumentMatchers.any())).thenReturn(false); Mockito.when(_planner.canHandle(vmProfile, plan, avoids)).thenReturn(false); + VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class); + Mockito.when(template.getArch()).thenReturn(CPU.CPUArch.amd64); + Mockito.when(vmProfile.getTemplate()).thenReturn(template); + Mockito.when(_clusterDao.listClustersByArchAndZoneId(dataCenterId, CPU.CPUArch.arm64)).thenReturn(null); DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, null); assertNull("Planner cannot handle, destination should be null! ", dest); } @@ -326,6 +335,10 @@ public void emptyClusterListTest() throws InsufficientServerCapacityException, A Mockito.when(_planner.canHandle(vmProfile, plan, avoids)).thenReturn(true); Mockito.when(((DeploymentClusterPlanner) _planner).orderClusters(vmProfile, plan, avoids)).thenReturn(null); + VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class); + Mockito.when(template.getArch()).thenReturn(CPU.CPUArch.amd64); + Mockito.when(vmProfile.getTemplate()).thenReturn(template); + Mockito.when(_clusterDao.listClustersByArchAndZoneId(dataCenterId, CPU.CPUArch.arm64)).thenReturn(null); DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, null); assertNull("Planner cannot handle, destination should be null! ", dest); } diff --git a/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java b/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java index 1160bf2ac8e9..924d7df88961 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 void setup() throws Exception { 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 ec594f185c4f..d125291657a9 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 boolean isProviderSupportServiceInNetwork(long networkId, Service service 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 7832537fe719..64d813c9ba8b 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 @@ 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.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 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 void setup() throws Exception { 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 void testCreateGuestNetwork() throws InsufficientCapacityException, Resou 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 void testCreateGuestNetwork() throws InsufficientCapacityException, Resou 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 void testValidateBypassingPublicMtuPassedDuringNetworkTierCreationForVpcs 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 void testUpdatePublicInterfaceMtuViaNetworkTiersForVpcNetworks() { 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 void testCreateNetworkDnsVpcFailure() { 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 void testCheckAndUpdateNetworkSuccess() { 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 000000000000..49067f265c9a --- /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 deffb165f29f..54acaa58acce 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.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.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 void setup() throws NoSuchFieldException, IllegalAccessException { 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 void testCreateVpcNetwork() throws InsufficientCapacityException, Resourc 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 void testDisabledConfigCreateIpv6VpcOffering() { 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 void testCreateVpcDnsOfferingServiceFailure() { 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 void testCreateVpcDnsIpv6OfferingFailure() { 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/storage/VolumeApiServiceImplTest.java b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java index 48a41487062f..489431863a23 100644 --- a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java +++ b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java @@ -84,7 +84,6 @@ import org.springframework.test.util.ReflectionTestUtils; import com.cloud.api.query.dao.ServiceOfferingJoinDao; -import com.cloud.api.query.vo.ServiceOfferingJoinVO; import com.cloud.configuration.Resource; import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.DataCenterVO; @@ -1370,10 +1369,8 @@ private void prepareAndRunTestOfIsNotPossibleToResize(Type volumeType, Long root when(volume.getTemplateId()).thenReturn(1l); DiskOfferingVO diskOffering = Mockito.mock(DiskOfferingVO.class); - - ServiceOfferingJoinVO serviceOfferingJoinVO = Mockito.mock(ServiceOfferingJoinVO.class); - when(serviceOfferingJoinVO.getRootDiskSize()).thenReturn(rootDisk); - when(serviceOfferingJoinDao.findById(anyLong())).thenReturn(serviceOfferingJoinVO); + when(diskOffering.isComputeOnly()).thenReturn(true); + when(diskOffering.getDiskSize()).thenReturn(rootDisk); VMTemplateVO template = Mockito.mock(VMTemplateVO.class); when(template.getFormat()).thenReturn(imageFormat); diff --git a/server/src/test/java/com/cloud/user/AccountManagetImplTestBase.java b/server/src/test/java/com/cloud/user/AccountManagetImplTestBase.java index 7f9fa488471a..e97fddc02622 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.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 829f0c9cb200..39155986941e 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.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/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index 60e00e2ccf51..32cc5ba0fa88 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -497,7 +497,7 @@ private void verifyMethodsThatAreAlwaysExecuted() throws ResourceUnavailableExce Mockito.verify(userVmManagerImpl).getSecurityGroupIdList(updateVmCommand); Mockito.verify(userVmManagerImpl).updateVirtualMachine(nullable(Long.class), nullable(String.class), nullable(String.class), nullable(Boolean.class), - nullable(Boolean.class), nullable(Long.class), + nullable(Boolean.class), nullable(Boolean.class), nullable(Long.class), nullable(String.class), nullable(Long.class), nullable(String.class), nullable(Boolean.class), nullable(HTTPMethod.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(List.class), nullable(Map.class)); @@ -508,7 +508,7 @@ private void configureDoNothingForMethodsThatWeDoNotWantToTest() throws Resource Mockito.doNothing().when(userVmManagerImpl).validateInputsAndPermissionForUpdateVirtualMachineCommand(updateVmCommand); Mockito.doReturn(new ArrayList()).when(userVmManagerImpl).getSecurityGroupIdList(updateVmCommand); Mockito.lenient().doReturn(Mockito.mock(UserVm.class)).when(userVmManagerImpl).updateVirtualMachine(Mockito.anyLong(), Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean(), - Mockito.anyBoolean(), Mockito.anyLong(), + Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyBoolean(), Mockito.any(HTTPMethod.class), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyList(), Mockito.anyMap()); } diff --git a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java index 2a1f4fffbf83..8f05b716725a 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 NetworkOfferingVO createNetworkOffering(String name, String displayText, 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 68ad250a95e5..7f4344f30e42 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 Network createPrivateNetwork(final long networkOfferingId, final String n 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 5802b88a6cf7..7f94405ffdc4 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 boolean isProviderSupportServiceInNetwork(long networkId, Service service 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 d5192644e86a..6746c5ecbf82 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 boolean areServicesSupportedInNetwork(long networkId, Service... services return false; } + @Override + public boolean isAnyServiceSupportedInNetwork(long networkId, Provider provider, Service... services) { + return false; + } + /* (non-Javadoc) * @see com.cloud.network.dao.NetworkServiceMapDao#canProviderSupportServiceInNetwork(long, com.cloud.network.Network.Service, com.cloud.network.Network.Provider) */ diff --git a/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingDaoImpl.java b/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingDaoImpl.java index f0ff48224762..d0a2d0c1dba3 100644 --- a/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingDaoImpl.java +++ b/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingDaoImpl.java @@ -48,4 +48,9 @@ public NetUtils.InternetProtocol getVpcOfferingInternetProtocol(long offeringId) 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 679324fed2f9..b5c842b8806a 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 void testSetupAccountOwnerNotSharedNeitherNetworkSystem() { 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/network/router/deployment/VpcRouterDeploymentDefinitionTest.java b/server/src/test/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinitionTest.java index a355ad21f2ba..d3ab6d8904ba 100644 --- a/server/src/test/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinitionTest.java +++ b/server/src/test/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinitionTest.java @@ -269,7 +269,7 @@ public void testCreateVpcRouterNetworks() { public void testFindSourceNatIP() throws InsufficientAddressCapacityException, ConcurrentOperationException { // Prepare final PublicIp publicIp = mock(PublicIp.class); - when(vpcMgr.assignSourceNatIpAddressToVpc(mockOwner, mockVpc)).thenReturn(publicIp); + when(vpcMgr.assignSourceNatIpAddressToVpc(mockOwner, mockVpc, null)).thenReturn(publicIp); deployment.isPublicNetwork = true; // Execute 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 838bb3dadcb4..25b4bdda45f1 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 void tearDown() { 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 void createSharedNtwkOffWithVlan() { 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 void createSharedNtwkOffWithNoVlan() { 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 void createSharedNtwkOffWithSpecifyIpRanges() { 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 void createIsolatedNtwkOffWithNoVlan() { 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 void createIsolatedNtwkOffWithVlan() { 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 void createIsolatedNtwkOffWithSpecifyIpRangesAndSourceNat() { 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 void createIsolatedNtwkOffWithSpecifyIpRangesAndNoSourceNat() { 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 void createVpcNtwkOff() { 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 void createVpcNtwkOffWithNetscaler() { 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/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/AjaxFIFOImageCache.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/AjaxFIFOImageCache.java index 5a0a29977b53..1b94578d1e00 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/AjaxFIFOImageCache.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/AjaxFIFOImageCache.java @@ -21,10 +21,11 @@ import java.util.List; import java.util.Map; -import com.cloud.consoleproxy.util.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class AjaxFIFOImageCache { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); private List fifoQueue; private Map cache; diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxy.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxy.java index c841f76540d2..22922f43f930 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxy.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxy.java @@ -39,17 +39,19 @@ import org.apache.logging.log4j.core.config.Configurator; import org.eclipse.jetty.websocket.api.Session; -import com.cloud.consoleproxy.util.Logger; import com.cloud.utils.PropertiesUtil; import com.google.gson.Gson; import com.sun.net.httpserver.HttpServer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + /** * * ConsoleProxy, singleton class that manages overall activities in console proxy process. To make legacy code work, we still */ public class ConsoleProxy { - protected static Logger LOGGER = Logger.getLogger(ConsoleProxy.class); + protected static Logger LOGGER = LogManager.getLogger(ConsoleProxy.class); public static final int KEYBOARD_RAW = 0; public static final int KEYBOARD_COOKED = 1; @@ -280,7 +282,6 @@ public static void ensureRoute(String address) { public static void startWithContext(Properties conf, Object context, byte[] ksBits, String ksPassword, String password, Boolean isSourceIpCheckEnabled) { setEncryptorPassword(password); configLog4j(); - Logger.setFactory(new ConsoleProxyLoggerFactory()); LOGGER.info("Start console proxy with context"); if (conf != null) { @@ -427,7 +428,6 @@ private static void startupHttpCmdPort() { public static void main(String[] argv) { standaloneStart = true; configLog4j(); - Logger.setFactory(new ConsoleProxyLoggerFactory()); InputStream confs = ConsoleProxy.class.getResourceAsStream("/conf/consoleproxy.properties"); Properties conf = new Properties(); diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java index e42917db6aa0..bfd25188c652 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java @@ -32,10 +32,11 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; -import com.cloud.consoleproxy.util.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class ConsoleProxyAjaxHandler implements HttpHandler { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); public ConsoleProxyAjaxHandler() { } diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java index af200b0a0e8f..bb5b9f6ec310 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyAjaxImageHandler.java @@ -28,10 +28,11 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; -import com.cloud.consoleproxy.util.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class ConsoleProxyAjaxImageHandler implements HttpHandler { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); @Override public void handle(HttpExchange t) throws IOException { diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java index b178f0d5d68f..548b1a992610 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyBaseServerFactoryImpl.java @@ -23,10 +23,11 @@ import com.sun.net.httpserver.HttpServer; -import com.cloud.consoleproxy.util.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class ConsoleProxyBaseServerFactoryImpl implements ConsoleProxyServerFactory { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); @Override public void init(byte[] ksBits, String ksPassword) { diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyClientParam.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyClientParam.java index aa1f2223a8c7..01c4fa6480e8 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyClientParam.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyClientParam.java @@ -39,8 +39,16 @@ public class ConsoleProxyClientParam { private String password; private String websocketUrl; + /** + * IP that has generated the console endpoint + */ private String sourceIP; + /** + * IP of the client that has connected to the console + */ + private String clientIp; + private String sessionUuid; /** @@ -204,4 +212,12 @@ public String getClientProvidedExtraSecurityToken() { public void setClientProvidedExtraSecurityToken(String clientProvidedExtraSecurityToken) { this.clientProvidedExtraSecurityToken = clientProvidedExtraSecurityToken; } + + public String getClientIp() { + return clientIp; + } + + public void setClientIp(String clientIp) { + this.clientIp = clientIp; + } } diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java index 400eb2b99849..606b4509512b 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyCmdHandler.java @@ -24,10 +24,11 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; -import com.cloud.consoleproxy.util.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class ConsoleProxyCmdHandler implements HttpHandler { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); @Override public void handle(HttpExchange t) throws IOException { diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java index fb9d0794c227..48ac5f44ff23 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyHttpHandlerHelper.java @@ -19,10 +19,11 @@ import java.util.HashMap; import java.util.Map; -import com.cloud.consoleproxy.util.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class ConsoleProxyHttpHandlerHelper { - protected static Logger LOGGER = Logger.getLogger(ConsoleProxyHttpHandlerHelper.class); + protected static Logger LOGGER = LogManager.getLogger(ConsoleProxyHttpHandlerHelper.class); public static Map getQueryMap(String query) { String[] params = query.split("&"); diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java deleted file mode 100644 index 74e393f64d81..000000000000 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyLoggerFactory.java +++ /dev/null @@ -1,104 +0,0 @@ -// 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.consoleproxy; - -import com.cloud.consoleproxy.util.Logger; -import com.cloud.consoleproxy.util.LoggerFactory; -import org.apache.logging.log4j.LogManager; - -public class ConsoleProxyLoggerFactory implements LoggerFactory { - public ConsoleProxyLoggerFactory() { - } - - @Override - public Logger getLogger(Class clazz) { - return new Log4jLogger(LogManager.getLogger(clazz)); - } - - public static class Log4jLogger extends Logger { - private org.apache.logging.log4j.Logger logger; - - public Log4jLogger(org.apache.logging.log4j.Logger logger) { - this.logger = logger; - } - - @Override - public boolean isTraceEnabled() { - return logger.isTraceEnabled(); - } - - @Override - public boolean isDebugEnabled() { - return logger.isDebugEnabled(); - } - - @Override - public boolean isInfoEnabled() { - return logger.isInfoEnabled(); - } - - @Override - public void trace(Object message) { - logger.trace(message); - } - - @Override - public void trace(Object message, Throwable exception) { - logger.trace(message, exception); - } - - @Override - public void info(Object message) { - logger.info(message); - } - - @Override - public void info(Object message, Throwable exception) { - logger.info(message, exception); - } - - @Override - public void debug(Object message) { - logger.debug(message); - } - - @Override - public void debug(Object message, Throwable exception) { - logger.debug(message, exception); - } - - @Override - public void warn(Object message) { - logger.warn(message); - } - - @Override - public void warn(Object message, Throwable exception) { - logger.warn(message, exception); - } - - @Override - public void error(Object message) { - logger.error(message); - } - - @Override - public void error(Object message, Throwable exception) { - logger.error(message, exception); - } - } -} diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyMonitor.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyMonitor.java index 378072ad8045..3e224d8d4c42 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyMonitor.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyMonitor.java @@ -24,7 +24,8 @@ import java.util.Map; -import com.cloud.consoleproxy.util.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.Configurator; // @@ -33,7 +34,7 @@ // itself and the shell script will re-launch console proxy // public class ConsoleProxyMonitor { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); private String[] _argv; private Map _argMap = new HashMap(); diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVNCHandler.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVNCHandler.java index be0db7b8fb47..a9639d0b32e3 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVNCHandler.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVNCHandler.java @@ -23,7 +23,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import com.cloud.consoleproxy.util.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.websocket.api.Session; @@ -40,7 +41,7 @@ public class ConsoleProxyNoVNCHandler extends WebSocketHandler { private ConsoleProxyNoVncClient viewer = null; - protected Logger logger = Logger.getLogger(ConsoleProxyNoVNCHandler.class); + protected Logger logger = LogManager.getLogger(getClass()); public ConsoleProxyNoVNCHandler() { super(); @@ -82,15 +83,16 @@ public void onConnect(final Session session) throws IOException, InterruptedExce String ticket = queryMap.get("ticket"); String displayName = queryMap.get("displayname"); String ajaxSessionIdStr = queryMap.get("sess"); - String console_url = queryMap.get("consoleurl"); - String console_host_session = queryMap.get("sessionref"); - String vm_locale = queryMap.get("locale"); + String consoleUrl = queryMap.get("consoleurl"); + String consoleHostSession = queryMap.get("sessionref"); + String vmLocale = queryMap.get("locale"); String hypervHost = queryMap.get("hypervHost"); String username = queryMap.get("username"); String password = queryMap.get("password"); String sourceIP = queryMap.get("sourceIP"); String websocketUrl = queryMap.get("websocketUrl"); String sessionUuid = queryMap.get("sessionUuid"); + String clientIp = session.getRemoteAddress().getAddress().getHostAddress(); if (tag == null) tag = ""; @@ -104,7 +106,7 @@ public void onConnect(final Session session) throws IOException, InterruptedExce try { port = Integer.parseInt(portStr); } catch (NumberFormatException e) { - logger.warn("Invalid number parameter in query string: " + portStr); + logger.error("Invalid port value in query string: {}. Expected a number.", portStr, e); throw new IllegalArgumentException(e); } @@ -112,12 +114,12 @@ public void onConnect(final Session session) throws IOException, InterruptedExce try { ajaxSessionId = Long.parseLong(ajaxSessionIdStr); } catch (NumberFormatException e) { - logger.warn("Invalid number parameter in query string: " + ajaxSessionIdStr); + logger.error("Invalid ajaxSessionId (sess) value in query string: {}. Expected a number.", ajaxSessionIdStr, e); throw new IllegalArgumentException(e); } } - if (! checkSessionSourceIp(session, sourceIP)) { + if (!checkSessionSourceIp(session, sourceIP, clientIp)) { return; } @@ -129,14 +131,17 @@ public void onConnect(final Session session) throws IOException, InterruptedExce param.setClientTag(tag); param.setTicket(ticket); param.setClientDisplayName(displayName); - param.setClientTunnelUrl(console_url); - param.setClientTunnelSession(console_host_session); - param.setLocale(vm_locale); + param.setClientTunnelUrl(consoleUrl); + param.setClientTunnelSession(consoleHostSession); + param.setLocale(vmLocale); param.setHypervHost(hypervHost); param.setUsername(username); param.setPassword(password); param.setWebsocketUrl(websocketUrl); param.setSessionUuid(sessionUuid); + param.setSourceIP(sourceIP); + param.setClientIp(clientIp); + if (queryMap.containsKey("extraSecurityToken")) { param.setExtraSecurityToken(queryMap.get("extraSecurityToken")); } @@ -144,8 +149,9 @@ public void onConnect(final Session session) throws IOException, InterruptedExce param.setClientProvidedExtraSecurityToken(queryMap.get("extra")); } viewer = ConsoleProxy.getNoVncViewer(param, ajaxSessionIdStr, session); + logger.info("Viewer has been created successfully [session UUID: {}, client IP: {}].", sessionUuid, clientIp); } catch (Exception e) { - logger.warn("Failed to create viewer due to " + e.getMessage(), e); + logger.error("Failed to create viewer [session UUID: {}, client IP: {}] due to {}.", sessionUuid, clientIp, e.getMessage(), e); return; } finally { if (viewer == null) { @@ -154,32 +160,35 @@ public void onConnect(final Session session) throws IOException, InterruptedExce } } - private boolean checkSessionSourceIp(final Session session, final String sourceIP) throws IOException { - // Verify source IP - String sessionSourceIP = session.getRemoteAddress().getAddress().getHostAddress(); - logger.info("Get websocket connection request from remote IP : " + sessionSourceIP); - if (ConsoleProxy.isSourceIpCheckEnabled && (sessionSourceIP == null || ! sessionSourceIP.equals(sourceIP))) { - logger.warn("Failed to access console as the source IP to request the console is " + sourceIP); + private boolean checkSessionSourceIp(final Session session, final String sourceIP, String sessionSourceIP) throws IOException { + logger.info("Verifying session source IP {} from WebSocket connection request.", sessionSourceIP); + if (ConsoleProxy.isSourceIpCheckEnabled && (sessionSourceIP == null || !sessionSourceIP.equals(sourceIP))) { + logger.warn("Failed to access console as the source IP to request the console is {}.", sourceIP); session.disconnect(); return false; } + logger.debug("Session source IP {} has been verified successfully.", sessionSourceIP); return true; } @OnWebSocketClose public void onClose(Session session, int statusCode, String reason) throws IOException, InterruptedException { + String sessionSourceIp = session.getRemoteAddress().getAddress().getHostAddress(); + logger.debug("Closing WebSocket session [source IP: {}, status code: {}].", sessionSourceIp, statusCode); if (viewer != null) { ConsoleProxy.removeViewer(viewer); } + logger.debug("WebSocket session [source IP: {}, status code: {}] closed successfully.", sessionSourceIp, statusCode); } @OnWebSocketFrame public void onFrame(Frame f) throws IOException { + logger.trace("Sending client [ID: {}] frame of {} bytes.", viewer.getClientId(), f.getPayloadLength()); viewer.sendClientFrame(f); } @OnWebSocketError public void onError(Throwable cause) { - logger.error("Error on websocket", cause); + logger.error("Error on WebSocket [client ID: {}, session UUID: {}].", cause, viewer.getClientId(), viewer.getSessionUuid()); } } diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVNCServer.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVNCServer.java index f65754169f64..3d94ed26b0ba 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVNCServer.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVNCServer.java @@ -22,7 +22,8 @@ import java.nio.file.Path; import java.security.KeyStore; -import com.cloud.consoleproxy.util.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -34,7 +35,7 @@ public class ConsoleProxyNoVNCServer { - protected static Logger LOGGER = Logger.getLogger(ConsoleProxyNoVNCServer.class); + protected static Logger LOGGER = LogManager.getLogger(ConsoleProxyNoVNCServer.class); public static final int WS_PORT = 8080; public static final int WSS_PORT = 8443; private static final String VNC_CONF_FILE_LOCATION = "/root/vncport"; diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVncClient.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVncClient.java index cf9e3cfddccd..fece5bfaa221 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVncClient.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVncClient.java @@ -75,9 +75,9 @@ public boolean isHostConnected() { @Override public boolean isFrontEndAlive() { - if (!connectionAlive || System.currentTimeMillis() - - getClientLastFrontEndActivityTime() > ConsoleProxy.VIEWER_LINGER_SECONDS * 1000) { - logger.info("Front end has been idle for too long"); + long unusedTime = System.currentTimeMillis() - getClientLastFrontEndActivityTime(); + if (!connectionAlive || unusedTime > ConsoleProxy.VIEWER_LINGER_SECONDS * 1000) { + logger.info("Front end has been idle for too long ({} ms).", unusedTime); return false; } return true; @@ -95,23 +95,24 @@ public void initClient(ConsoleProxyClientParam param) { client = new NoVncClient(); connectionAlive = true; this.sessionUuid = param.getSessionUuid(); + String clientSourceIp = param.getClientIp(); + logger.debug("Initializing client from IP {}.", clientSourceIp); updateFrontEndActivityTime(); Thread worker = new Thread(new Runnable() { public void run() { try { - String tunnelUrl = param.getClientTunnelUrl(); String tunnelSession = param.getClientTunnelSession(); String websocketUrl = param.getWebsocketUrl(); connectClientToVNCServer(tunnelUrl, tunnelSession, websocketUrl); - - authenticateToVNCServer(); + authenticateToVNCServer(clientSourceIp); int readBytes; byte[] b; while (connectionAlive) { + logger.trace("Connection with client [{}] [IP: {}] is alive.", clientId, clientSourceIp); if (client.isVncOverWebSocketConnection()) { if (client.isVncOverWebSocketConnectionOpen()) { updateFrontEndActivityTime(); @@ -122,7 +123,7 @@ public void run() { int nextBytes = client.getNextBytes(); bytesArr = new byte[nextBytes]; client.readBytes(bytesArr, nextBytes); - logger.trace(String.format("Read [%s] bytes from client [%s]", nextBytes, clientId)); + logger.trace("Read [{}] bytes from client [{}].", nextBytes, clientId); if (nextBytes > 0) { session.getRemote().sendBytes(ByteBuffer.wrap(bytesArr)); updateFrontEndActivityTime(); @@ -132,7 +133,7 @@ public void run() { } else { b = new byte[100]; readBytes = client.read(b); - logger.trace(String.format("Read [%s] bytes from client [%s]", readBytes, clientId)); + logger.trace("Read [{}] bytes from client [{}].", readBytes, clientId); if (readBytes == -1 || (readBytes > 0 && !sendReadBytesToNoVNC(b, readBytes))) { connectionAlive = false; } @@ -143,7 +144,7 @@ public void run() { logger.error("Error on sleep for vnc sessions", e); } } - logger.info(String.format("Connection with client [%s] is dead.", clientId)); + logger.info("Connection with client [{}] [IP: {}] is dead.", clientId, clientSourceIp); } catch (IOException e) { logger.error("Error on VNC client", e); } @@ -158,7 +159,7 @@ private boolean sendReadBytesToNoVNC(byte[] b, int readBytes) { session.getRemote().sendBytes(ByteBuffer.wrap(b, 0, readBytes)); updateFrontEndActivityTime(); } catch (WebSocketException | IOException e) { - logger.debug("Connection exception", e); + logger.error("VNC server connection exception.", e); return false; } return true; @@ -176,20 +177,24 @@ private boolean sendReadBytesToNoVNC(byte[] b, int readBytes) { * * Reference: https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#7protocol-messages */ - private void authenticateToVNCServer() throws IOException { + private void authenticateToVNCServer(String clientSourceIp) throws IOException { if (client.isVncOverWebSocketConnection()) { + logger.debug("Authentication skipped for client [{}] [IP: {}] to VNC server due to WebSocket protocol usage.", clientId, clientSourceIp); return; } if (!client.isVncOverNioSocket()) { + logger.debug("Authenticating client [{}] [IP: {}] to VNC server.", clientId, clientSourceIp); String ver = client.handshake(); session.getRemote().sendBytes(ByteBuffer.wrap(ver.getBytes(), 0, ver.length())); byte[] b = client.authenticateTunnel(getClientHostPassword()); session.getRemote().sendBytes(ByteBuffer.wrap(b, 0, 4)); } else { + logger.debug("Authenticating client [{}] [IP: {}] to VNC server through NIO Socket.", clientId, clientSourceIp); authenticateVNCServerThroughNioSocket(); } + logger.debug("Client [{}] [IP: {}] has been authenticated successfully to VNC server.", clientId, clientSourceIp); } /** @@ -233,9 +238,6 @@ protected void handshakeProtocolVersion() { protected void authenticateVNCServerThroughNioSocket() { handshakePhase(); initialisationPhase(); - if (logger.isDebugEnabled()) { - logger.debug("Authenticated successfully"); - } } /** @@ -289,8 +291,7 @@ private void connectClientToVNCServer(String tunnelUrl, String tunnelSession, St logger.info(String.format("Connect to VNC over websocket URL: %s", websocketUrl)); ConsoleProxy.ensureRoute(NetUtils.extractHost(websocketUrl)); client.connectToWebSocket(websocketUrl, session); - } else if (tunnelUrl != null && !tunnelUrl.isEmpty() && tunnelSession != null - && !tunnelSession.isEmpty()) { + } else if (StringUtils.isNotBlank(tunnelUrl) && StringUtils.isNotBlank(tunnelSession)) { URI uri = new URI(tunnelUrl); logger.info(String.format("Connect to VNC server via tunnel. url: %s, session: %s", tunnelUrl, tunnelSession)); @@ -304,8 +305,10 @@ private void connectClientToVNCServer(String tunnelUrl, String tunnelSession, St ConsoleProxy.ensureRoute(getClientHostAddress()); client.connectTo(getClientHostAddress(), getClientHostPort()); } + + logger.info("Connection to VNC server has been established successfully."); } catch (Throwable e) { - logger.error("Unexpected exception", e); + logger.error("Unexpected exception while connecting to VNC server.", e); } } @@ -370,6 +373,7 @@ public long getClientCreateTime() { } public void updateFrontEndActivityTime() { + logger.trace("Updating last front end activity time."); lastFrontEndActivityTime = System.currentTimeMillis(); } diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java index 949e632786c6..8764326b5032 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyResourceHandler.java @@ -28,10 +28,11 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; -import com.cloud.consoleproxy.util.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class ConsoleProxyResourceHandler implements HttpHandler { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); static Map s_mimeTypes; static { diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java index 0103d9fa70eb..e2ec7df5d69e 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyThumbnailHandler.java @@ -32,10 +32,11 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; -import com.cloud.consoleproxy.util.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class ConsoleProxyThumbnailHandler implements HttpHandler { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); public ConsoleProxyThumbnailHandler() { } diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/rdp/RdpBufferedImageCanvas.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/rdp/RdpBufferedImageCanvas.java index 7fd19a15d2fe..3a40b79437ba 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/rdp/RdpBufferedImageCanvas.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/rdp/RdpBufferedImageCanvas.java @@ -25,10 +25,12 @@ import com.cloud.consoleproxy.ConsoleProxyRdpClient; import com.cloud.consoleproxy.util.ImageHelper; -import com.cloud.consoleproxy.util.Logger; import com.cloud.consoleproxy.util.TileInfo; import com.cloud.consoleproxy.vnc.FrameBufferCanvas; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import common.BufferedImageCanvas; public class RdpBufferedImageCanvas extends BufferedImageCanvas implements FrameBufferCanvas { @@ -36,7 +38,7 @@ public class RdpBufferedImageCanvas extends BufferedImageCanvas implements Frame * */ private static final long serialVersionUID = 1L; - protected Logger logger = Logger.getLogger(RdpBufferedImageCanvas.class); + protected Logger logger = LogManager.getLogger(RdpBufferedImageCanvas.class); private final ConsoleProxyRdpClient _rdpClient; diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/util/Logger.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/util/Logger.java deleted file mode 100644 index 042d64977c2b..000000000000 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/util/Logger.java +++ /dev/null @@ -1,223 +0,0 @@ -// 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.consoleproxy.util; - -// logger facility for dynamic switch between console logger used in Applet and log4j based logger -public class Logger { - private static LoggerFactory factory = null; - - public static final int LEVEL_TRACE = 1; - public static final int LEVEL_DEBUG = 2; - public static final int LEVEL_INFO = 3; - public static final int LEVEL_WARN = 4; - public static final int LEVEL_ERROR = 5; - - private Class clazz; - private Logger logger; - - private static int level = LEVEL_INFO; - - public static Logger getLogger(Class clazz) { - return new Logger(clazz); - } - - public static void setFactory(LoggerFactory f) { - factory = f; - } - - public static void setLevel(int l) { - level = l; - } - - public Logger(Class clazz) { - this.clazz = clazz; - } - - protected Logger() { - } - - public boolean isTraceEnabled() { - if (factory != null) { - if (logger == null) - logger = factory.getLogger(clazz); - - return logger.isTraceEnabled(); - } - return level <= LEVEL_TRACE; - } - - public boolean isDebugEnabled() { - if (factory != null) { - if (logger == null) - logger = factory.getLogger(clazz); - - return logger.isDebugEnabled(); - } - return level <= LEVEL_DEBUG; - } - - public boolean isInfoEnabled() { - if (factory != null) { - if (logger == null) - logger = factory.getLogger(clazz); - - return logger.isInfoEnabled(); - } - return level <= LEVEL_INFO; - } - - public void trace(Object message) { - - if (factory != null) { - if (logger == null) - logger = factory.getLogger(clazz); - - logger.trace(message); - } else { - if (level <= LEVEL_TRACE) - System.out.println(message); - } - } - - public void trace(Object message, Throwable exception) { - if (factory != null) { - if (logger == null) - logger = factory.getLogger(clazz); - - logger.trace(message, exception); - } else { - if (level <= LEVEL_TRACE) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } - - public void info(Object message) { - if (factory != null) { - if (logger == null) - logger = factory.getLogger(clazz); - - logger.info(message); - } else { - if (level <= LEVEL_INFO) - System.out.println(message); - } - } - - public void info(Object message, Throwable exception) { - if (factory != null) { - if (logger == null) - logger = factory.getLogger(clazz); - - logger.info(message, exception); - } else { - if (level <= LEVEL_INFO) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } - - public void debug(Object message) { - if (factory != null) { - if (logger == null) - logger = factory.getLogger(clazz); - - logger.debug(message); - } else { - if (level <= LEVEL_DEBUG) - System.out.println(message); - } - } - - public void debug(Object message, Throwable exception) { - if (factory != null) { - if (logger == null) - logger = factory.getLogger(clazz); - - logger.debug(message, exception); - } else { - if (level <= LEVEL_DEBUG) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } - - public void warn(Object message) { - if (factory != null) { - if (logger == null) - logger = factory.getLogger(clazz); - - logger.warn(message); - } else { - if (level <= LEVEL_WARN) - System.out.println(message); - } - } - - public void warn(Object message, Throwable exception) { - if (factory != null) { - if (logger == null) - logger = factory.getLogger(clazz); - - logger.warn(message, exception); - } else { - if (level <= LEVEL_WARN) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } - - public void error(Object message) { - if (factory != null) { - if (logger == null) - logger = factory.getLogger(clazz); - - logger.error(message); - } else { - if (level <= LEVEL_ERROR) - System.out.println(message); - } - } - - public void error(Object message, Throwable exception) { - if (factory != null) { - if (logger == null) - logger = factory.getLogger(clazz); - - logger.error(message, exception); - } else { - if (level <= LEVEL_ERROR) { - System.out.println(message); - if (exception != null) { - exception.printStackTrace(System.out); - } - } - } - } -} diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/util/RawHTTP.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/util/RawHTTP.java index bc47ca03d122..99945c8b4caa 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/util/RawHTTP.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/util/RawHTTP.java @@ -38,6 +38,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + // // This file is originally from XenConsole with modifications // @@ -48,7 +51,7 @@ * connections and import/export operations. */ public final class RawHTTP { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); private static final Pattern END_PATTERN = Pattern.compile("^\r\n$"); private static final Pattern HEADER_PATTERN = Pattern.compile("^([A-Z_a-z0-9-]+):\\s*(.*)\r\n$"); diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java index 9b86a8fbc66b..0a168362ec1c 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/BufferedImageCanvas.java @@ -27,16 +27,18 @@ import java.util.List; import com.cloud.consoleproxy.util.ImageHelper; -import com.cloud.consoleproxy.util.Logger; import com.cloud.consoleproxy.util.TileInfo; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + /** * A BuffereImageCanvas component represents frame buffer image on * the screen. It also notifies its subscribers when screen is repainted. */ public class BufferedImageCanvas extends Canvas implements FrameBufferCanvas { private static final long serialVersionUID = 1L; - protected Logger logger = Logger.getLogger(BufferedImageCanvas.class); + protected Logger logger = LogManager.getLogger(BufferedImageCanvas.class); // Offline screen buffer private BufferedImage offlineImage; diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/NoVncClient.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/NoVncClient.java index c5764a994c59..e4bb93711b92 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/NoVncClient.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/NoVncClient.java @@ -31,7 +31,6 @@ import java.util.Arrays; import java.util.List; -import com.cloud.consoleproxy.util.Logger; import com.cloud.consoleproxy.util.RawHTTP; import com.cloud.consoleproxy.vnc.network.NioSocket; import com.cloud.consoleproxy.vnc.network.NioSocketHandler; @@ -42,7 +41,11 @@ import com.cloud.consoleproxy.websocket.WebSocketReverseProxy; import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; + import org.apache.commons.lang3.BooleanUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import org.eclipse.jetty.websocket.api.Session; import javax.crypto.BadPaddingException; @@ -54,7 +57,7 @@ import javax.crypto.spec.DESKeySpec; public class NoVncClient { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); private Socket socket; private DataInputStream is; @@ -79,6 +82,7 @@ public void connectTo(String host, int port, String path, String session, boolea port = 80; } + logger.info("Connecting to VNC server {}:{} ...", host, port); RawHTTP tunnel = new RawHTTP("CONNECT", host, port, path, session, useSSL); socket = tunnel.connect(); setTunnelSocketStreams(); @@ -86,7 +90,7 @@ public void connectTo(String host, int port, String path, String session, boolea public void connectTo(String host, int port) { // Connect to server - logger.info(String.format("Connecting to VNC server %s:%s ...", host, port)); + logger.info("Connecting to VNC server {}:{} ...", host, port); try { NioSocket nioSocket = new NioSocket(host, port); this.nioSocketConnection = new NioSocketHandlerImpl(nioSocket); @@ -175,8 +179,9 @@ public byte[] authenticateTunnel(String password) is.readFully(buf); String reason = new String(buf, RfbConstants.CHARSET); - logger.error("Authentication to VNC server is failed. Reason: " + reason); - throw new RuntimeException("Authentication to VNC server is failed. Reason: " + reason); + String msg = String.format("Authentication to VNC server has failed. Reason: %s", reason); + logger.error(msg); + throw new RuntimeException(msg); } case RfbConstants.NO_AUTH: { @@ -191,9 +196,9 @@ public byte[] authenticateTunnel(String password) } default: - logger.error("Unsupported VNC protocol authorization scheme, scheme code: " + authType + "."); - throw new RuntimeException( - "Unsupported VNC protocol authorization scheme, scheme code: " + authType + "."); + String msg = String.format("Unsupported VNC protocol authorization scheme, scheme code: %d.", authType); + logger.error(msg); + throw new RuntimeException(msg); } // Since we've taken care of the auth, we tell the client that there's no auth // going on diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/VncClient.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/VncClient.java index e5a9918d9353..05bd01d390e2 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/VncClient.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/VncClient.java @@ -33,13 +33,15 @@ import javax.crypto.spec.DESKeySpec; import com.cloud.consoleproxy.ConsoleProxyClientListener; -import com.cloud.consoleproxy.util.Logger; import com.cloud.consoleproxy.util.RawHTTP; import com.cloud.consoleproxy.vnc.packet.client.KeyboardEventPacket; import com.cloud.consoleproxy.vnc.packet.client.MouseEventPacket; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class VncClient { - protected static Logger LOGGER = Logger.getLogger(VncClient.class); + protected static Logger LOGGER = LogManager.getLogger(VncClient.class); private Socket socket; private DataInputStream is; diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/VncClientPacketSender.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/VncClientPacketSender.java index 12daca619cec..3d055b4dbb48 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/VncClientPacketSender.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/VncClientPacketSender.java @@ -27,7 +27,6 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; -import com.cloud.consoleproxy.util.Logger; import com.cloud.consoleproxy.vnc.packet.client.ClientPacket; import com.cloud.consoleproxy.vnc.packet.client.FramebufferUpdateRequestPacket; import com.cloud.consoleproxy.vnc.packet.client.KeyboardEventPacket; @@ -35,8 +34,11 @@ import com.cloud.consoleproxy.vnc.packet.client.SetEncodingsPacket; import com.cloud.consoleproxy.vnc.packet.client.SetPixelFormatPacket; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class VncClientPacketSender implements Runnable, PaintNotificationListener, KeyListener, MouseListener, MouseMotionListener, FrameBufferUpdateListener { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); // Queue for outgoing packets private final BlockingQueue queue = new ArrayBlockingQueue(30); diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java index effcb7b45998..bbd39754ac3e 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/VncServerPacketReceiver.java @@ -22,12 +22,14 @@ import java.io.IOException; import com.cloud.consoleproxy.ConsoleProxyClientListener; -import com.cloud.consoleproxy.util.Logger; import com.cloud.consoleproxy.vnc.packet.server.FramebufferUpdatePacket; import com.cloud.consoleproxy.vnc.packet.server.ServerCutText; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class VncServerPacketReceiver implements Runnable { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); private final VncScreenDescription screen; private BufferedImageCanvas canvas; diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/packet/server/AbstractRect.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/packet/server/AbstractRect.java index 2059278905b8..a56ee63d306f 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/packet/server/AbstractRect.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/packet/server/AbstractRect.java @@ -16,11 +16,12 @@ // under the License. package com.cloud.consoleproxy.vnc.packet.server; -import com.cloud.consoleproxy.util.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public abstract class AbstractRect implements Rect { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); protected final int x; protected final int y; diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/packet/server/ServerCutText.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/packet/server/ServerCutText.java index 79ed98cccd0e..76e47ab0676b 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/packet/server/ServerCutText.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/packet/server/ServerCutText.java @@ -19,11 +19,13 @@ import java.io.DataInputStream; import java.io.IOException; -import com.cloud.consoleproxy.util.Logger; import com.cloud.consoleproxy.vnc.RfbConstants; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class ServerCutText { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); private String content; diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/security/VncAuthSecurity.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/security/VncAuthSecurity.java index 29c29f8ff583..42f109b5cf3a 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/security/VncAuthSecurity.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/security/VncAuthSecurity.java @@ -16,7 +16,6 @@ // under the License. package com.cloud.consoleproxy.vnc.security; -import com.cloud.consoleproxy.util.Logger; import com.cloud.consoleproxy.vnc.NoVncClient; import com.cloud.consoleproxy.vnc.network.NioSocketHandler; import com.cloud.utils.exception.CloudRuntimeException; @@ -24,12 +23,15 @@ import java.io.IOException; import java.nio.ByteBuffer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class VncAuthSecurity implements VncSecurity { private final String vmPass; private static final int VNC_AUTH_CHALLENGE_SIZE = 16; - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); public VncAuthSecurity(String vmPass) { this.vmPass = vmPass; diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/security/VncTLSSecurity.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/security/VncTLSSecurity.java index 00497a378281..0bc1355c4fbf 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/security/VncTLSSecurity.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/vnc/security/VncTLSSecurity.java @@ -16,7 +16,6 @@ // under the License. package com.cloud.consoleproxy.vnc.security; -import com.cloud.consoleproxy.util.Logger; import com.cloud.consoleproxy.vnc.RfbConstants; import com.cloud.consoleproxy.vnc.network.NioSocketHandler; import com.cloud.consoleproxy.vnc.network.NioSocketSSLEngineManager; @@ -29,9 +28,12 @@ import java.security.GeneralSecurityException; import java.util.ArrayList; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class VncTLSSecurity implements VncSecurity { - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); private SSLContext ctx; private SSLEngine engine; diff --git a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/websocket/WebSocketReverseProxy.java b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/websocket/WebSocketReverseProxy.java index 582fb625f2c5..fecbaae38e81 100644 --- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/websocket/WebSocketReverseProxy.java +++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/websocket/WebSocketReverseProxy.java @@ -16,7 +16,6 @@ // under the License. package com.cloud.consoleproxy.websocket; -import com.cloud.consoleproxy.util.Logger; import org.eclipse.jetty.websocket.api.Session; import org.java_websocket.client.WebSocketClient; import org.java_websocket.drafts.Draft_6455; @@ -36,6 +35,9 @@ import java.security.cert.X509Certificate; import java.util.Collections; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + /** * Acts as a websocket reverse proxy between the remoteSession and the connected endpoint * - Connects to a websocket endpoint and sends the received data to the remoteSession endpoint @@ -51,7 +53,7 @@ public class WebSocketReverseProxy extends WebSocketClient { private static final DefaultExtension defaultExtension = new DefaultExtension(); private static final Draft_6455 draft = new Draft_6455(Collections.singletonList(defaultExtension), Collections.singletonList(protocol)); - protected Logger logger = Logger.getLogger(getClass()); + protected Logger logger = LogManager.getLogger(getClass()); private Session remoteSession; private void acceptAllCerts() { diff --git a/systemvm/debian/opt/cloud/bin/configure.py b/systemvm/debian/opt/cloud/bin/configure.py index 5215c2c6315f..21e125e3bb61 100755 --- a/systemvm/debian/opt/cloud/bin/configure.py +++ b/systemvm/debian/opt/cloud/bin/configure.py @@ -26,6 +26,7 @@ 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.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 @@ def add_rule(self): 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 @@ def __init__(self, obj, config): 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 @@ def __process_ip6(self, direction, rule_list): 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 AclRule(): 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 @@ def create(self): 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 @@ def flushAllowAllEgressRules(self): 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 @@ def flushAllIpv6Rules(self): 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 @@ def process(self): else: self.AclIP(self.dbag[item], self.config).create() - class CsIpv6Firewall(CsDataBag): """ Deal with IPv6 Firewall @@ -1335,11 +1575,22 @@ def process(self): 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 @@ def process(self): 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 1b3d1a763873..0d1c31ac14b1 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsAddress.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsAddress.py @@ -307,6 +307,8 @@ def __init__(self, dev, config): 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 @@ def post_configure(self, address): 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 @@ def post_configure(self, address): 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 @@ def mtu(self): 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 @@ def setup_router_control(self): 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 @@ def fw_router(self): 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 @@ def fw_vpcrouter(self): 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 @@ def post_config_change(self, method): self.fw_router() self.fw_vpcrouter() + self.fw_router_routing() + self.fw_vpcrouter_routing() cmdline = self.config.cmdline() @@ -688,7 +774,7 @@ def post_config_change(self, method): 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 000000000000..137279b74a9e --- /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 bfc5c1349983..a17f6ac4aa59 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 @@ def address(self): 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 @@ def is_vpc(self): 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 a6e84bb0b8cc..abbf23b4cdfc 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsDatabag.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsDatabag.py @@ -107,6 +107,18 @@ def get_vpccidr(self): 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 41b8b6441867..615c61d98e30 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py @@ -40,7 +40,7 @@ def get_dns(self): 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 c753350eaf52..d9950c9cd02a 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsNetfilter.py @@ -233,6 +233,57 @@ def add_ip6_chain(self, address_family, table, chain, hook, action): 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 000000000000..b284a74f8708 --- /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 1d320395d74c..712d15f1c064 100755 --- a/systemvm/debian/opt/cloud/bin/merge.py +++ b/systemvm/debian/opt/cloud/bin/merge.py @@ -37,6 +37,7 @@ import cs_remoteaccessvpn import cs_vpnusers import cs_staticroutes +import cs_bgppeers class DataBag: @@ -132,6 +133,8 @@ def process(self): 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 @@ def process_monitorservice(self, dbag): 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 47035920645e..6e5cba93e5be 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 77297f02c2c0..5156d77a6a62 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 f713ca42086e..801770fcc834 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 000000000000..124be678965c --- /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 2c369f283003..1e5cec7ef4c2 100644 --- a/test/integration/smoke/test_network_ipv6.py +++ b/test/integration/smoke/test_network_ipv6.py @@ -394,7 +394,7 @@ def getRouterProcessStatus(self, router, cmd): 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_vm_life_cycle.py b/test/integration/smoke/test_vm_life_cycle.py index c05ae2ad42ee..c7c9a01bd32c 100644 --- a/test/integration/smoke/test_vm_life_cycle.py +++ b/test/integration/smoke/test_vm_life_cycle.py @@ -1035,6 +1035,34 @@ def test_13_destroy_and_expunge_vm(self): return + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_14_destroy_vm_delete_protection(self): + """Test destroy Virtual Machine with delete protection + """ + + # Validate the following + # 1. Should not be able to delete the VM when delete protection is enabled + # 2. Should be able to delete the VM after disabling delete protection + + vm = VirtualMachine.create( + self.apiclient, + self.services["small"], + serviceofferingid=self.small_offering.id, + mode=self.services["mode"], + startvm=False + ) + + vm.update(self.apiclient, deleteprotection=True) + try: + vm.delete(self.apiclient) + self.fail("VM shouldn't get deleted with delete protection enabled") + except Exception as e: + self.debug("Expected exception: %s" % e) + + vm.update(self.apiclient, deleteprotection=False) + vm.delete(self.apiclient) + + return class TestSecuredVmMigration(cloudstackTestCase): diff --git a/test/integration/smoke/test_volumes.py b/test/integration/smoke/test_volumes.py index 7d64a27eaf2d..28a029adf70f 100644 --- a/test/integration/smoke/test_volumes.py +++ b/test/integration/smoke/test_volumes.py @@ -1038,6 +1038,33 @@ def test_13_migrate_volume_and_change_offering(self): ) return + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_14_delete_volume_delete_protection(self): + """Delete a Volume with delete protection + + # Validate the following + # 1. delete volume will fail when delete protection is enabled + # 2. delete volume is successful when delete protection is disabled + """ + + volume = Volume.create( + self.apiclient, + self.services, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering.id + ) + volume.update(self.apiclient, deleteprotection=True) + try: + volume.delete(self.apiclient) + self.fail("Volume delete should have failed with delete protection enabled") + except Exception as e: + self.debug("Volume delete failed as expected with error: %s" % e) + + volume.update(self.apiclient, deleteprotection=False) + volume.destroy(self.apiclient, expunge=True) + class TestVolumeEncryption(cloudstackTestCase): diff --git a/test/integration/smoke/test_vpc_ipv6.py b/test/integration/smoke/test_vpc_ipv6.py index efec43add7ca..bc05334a56ee 100644 --- a/test/integration/smoke/test_vpc_ipv6.py +++ b/test/integration/smoke/test_vpc_ipv6.py @@ -520,7 +520,7 @@ def getRouterProcessStatus(self, router, cmd): 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 1ec3b2f7dda7..ace0dbb33f3b 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -51,6 +51,7 @@ 'VirtualMachine': 'Virtual Machine', 'VM': 'Virtual Machine', 'Vnf': 'Virtual Network Functions', + 'GuestSubnet': 'Routing', 'Domain': 'Domain', 'Template': 'Template', 'Iso': 'ISO', @@ -280,7 +281,13 @@ '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 d1a6930d66ac..077cabf8d932 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 63407863110f..d391b5c4e192 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 9c2c5b120896..557434ea2ee3 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -1160,6 +1160,14 @@ def create(cls, apiclient, services, zoneid=None, account=None, return Volume(apiclient.createVolume(cmd).__dict__) + def update(self, apiclient, **kwargs): + """Updates the volume""" + + cmd = updateVolume.updateVolumeCmd() + cmd.id = self.id + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return (apiclient.updateVolume(cmd)) + @classmethod def create_custom_disk(cls, apiclient, services, account=None, domainid=None, diskofferingid=None, projectid=None): @@ -2835,6 +2843,10 @@ def create(cls, apiclient, services, **kwargs): 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 +3565,7 @@ def create(cls, apiclient, services, accountid=None, domainid=None, 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 +3592,10 @@ def create(cls, apiclient, services, accountid=None, domainid=None, 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 +3653,7 @@ def create(cls, apiclient, services, accountid=None, domainid=None, 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 +3707,13 @@ def list(cls, apiclient, **kwargs): 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 +5116,10 @@ def create(cls, apiclient, services): }) 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 +5230,13 @@ def list(cls, apiclient, **kwargs): 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 +7329,246 @@ def delete_deliveries(self, apiclient, **kwargs): [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 d3cbd421c2fa..f80eccf11590 100644 --- a/tools/marvin/marvin/lib/utils.py +++ b/tools/marvin/marvin/lib/utils.py @@ -43,11 +43,11 @@ 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 83e5561dc4d5..7336c038c897 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", @@ -371,11 +381,15 @@ "label.apply.tungsten.firewall.policy": "Apply Firewall Policy", "label.apply.tungsten.network.policy": "Apply Network Policy", "label.apply.tungsten.tag": "Apply tag", +"label.arch": "Arch", "label.archive": "Archive", "label.archived": "Archived", "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 +442,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 +482,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 +493,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 +548,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 +605,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,13 +664,16 @@ "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", "label.dedicated": "Dedicated", "label.dedicated.vlan.vni.ranges": "Dedicated VLAN/VNI ranges", +"label.dedicatedresources": "Dedicated resources", "label.default": "Default", "label.default.use": "Default use", "label.default.view": "Default view", @@ -661,8 +684,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 +703,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", @@ -715,6 +741,7 @@ "label.deleting.iso": "Deleting ISO", "label.deleting.snapshot": "Deleting Snapshot", "label.deleting.template": "Deleting Template", +"label.deleteprotection": "Delete protection", "label.demote.project.owner": "Demote Account to regular role", "label.demote.project.owner.user": "Demote User to regular role", "label.deny": "Deny", @@ -846,6 +873,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 +911,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 +1170,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 +1199,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 +1510,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 +1559,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 +1652,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 +1845,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 +1940,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 +2097,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 +2132,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 +2149,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 +2188,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 +2350,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 +2606,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 +2615,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 +2682,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 +2724,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 +2751,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 +2925,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 +3028,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 +3038,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 +3052,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 +3091,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 +3135,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.", @@ -3161,6 +3223,7 @@ "message.ip.address": "IP address : ", "message.ip.address.changes.effect.after.vm.restart": "IP address changes takes effect only after Instance restart.", "message.ip.v6.prefix.delete": "IPv6 prefix deleted", +"message.iso.arch": "Please select an ISO architecture", "message.iso.desc": "Disc image containing data or bootable media for OS.", "message.kubeconfig.cluster.not.available": "Kubernetes cluster kubeconfig not available currently.", "message.kubernetes.cluster.delete": "Please confirm that you want to destroy the cluster.", @@ -3279,6 +3342,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 +3375,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 +3411,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 +3449,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 +3460,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 +3473,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 +3510,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 +3534,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", @@ -3474,6 +3555,7 @@ "message.suspend.project": "Are you sure you want to suspend this project?", "message.sussess.discovering.feature": "Discovered all available features!", "message.switch.to": "Switched to", +"message.template.arch": "Please select a Template architecture.", "message.template.desc": "OS image that can be used to boot Instances.", "message.template.import.vm.temporary": "If a temporary Template is used, the reset Instance operation will not work after importing it.", "message.template.iso": "Please select a Template or ISO to continue.", @@ -3488,6 +3570,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 +3668,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/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json index 79333c100d31..f02aee747eb8 100644 --- a/ui/public/locales/pt_BR.json +++ b/ui/public/locales/pt_BR.json @@ -472,6 +472,7 @@ "label.dedicate.zone": "Zona dedicada", "label.dedicated": "Dedicado", "label.dedicated.vlan.vni.ranges": "Intervalo(s) de VLAN/VNI dedicados", +"label.dedicatedresources": "Recursos dedicados", "label.default": "Padr\u00e3o", "label.default.use": "Uso padr\u00e3o", "label.default.view": "Visualiza\u00e7\u00e3o padr\u00e3o", diff --git a/ui/src/components/view/DetailsTab.vue b/ui/src/components/view/DetailsTab.vue index 017d304e39b5..f5f180c8f195 100644 --- a/ui/src/components/view/DetailsTab.vue +++ b/ui/src/components/view/DetailsTab.vue @@ -23,6 +23,11 @@ + + + + + -