diff --git a/api/src/main/java/com/cloud/dc/DataCenter.java b/api/src/main/java/com/cloud/dc/DataCenter.java index 4a8f6d9d0a0..d6564158b88 100644 --- a/api/src/main/java/com/cloud/dc/DataCenter.java +++ b/api/src/main/java/com/cloud/dc/DataCenter.java @@ -32,6 +32,10 @@ public interface DataCenter extends InfrastructureEntity, Grouping, Partition { Basic, Advanced, } + public enum Type { + Core, Edge, + } + String getDns1(); String getDns2(); @@ -83,4 +87,6 @@ public interface DataCenter extends InfrastructureEntity, Grouping, Partition { boolean isLocalStorageEnabled(); int getSortKey(); + + Type getType(); } 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 653b38e6ea3..9548ddfc6b4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -243,6 +243,7 @@ public class ApiConstants { public static final String IP_TOTAL = "iptotal"; public static final String IS_CLEANUP_REQUIRED = "iscleanuprequired"; public static final String IS_DYNAMIC = "isdynamic"; + public static final String IS_EDGE = "isedge"; public static final String IS_EXTRACTABLE = "isextractable"; public static final String IS_FEATURED = "isfeatured"; public static final String IS_PORTABLE = "isportable"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/CreatePodCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/CreatePodCmd.java index 33fe6c759b2..b15854ca875 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/CreatePodCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/CreatePodCmd.java @@ -50,16 +50,16 @@ public class CreatePodCmd extends BaseCmd { description = "the Zone ID in which the Pod will be created") private Long zoneId; - @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, required = true, description = "the starting IP address for the Pod") + @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "the starting IP address for the Pod") private String startIp; @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, description = "the ending IP address for the Pod") private String endIp; - @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, required = true, description = "the netmask for the Pod") + @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "the netmask for the Pod") private String netmask; - @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, required = true, description = "the gateway for the Pod") + @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, description = "the gateway for the Pod") private String gateway; @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "Allocation state of this Pod for allocation of new resources") diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java index 263d3c824b0..aca3e00d095 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java @@ -87,6 +87,10 @@ public class CreateZoneCmd extends BaseCmd { @Parameter(name = ApiConstants.LOCAL_STORAGE_ENABLED, type = CommandType.BOOLEAN, description = "true if local storage offering enabled, false otherwise") private Boolean localStorageEnabled; + @Parameter(name = ApiConstants.IS_EDGE, type = CommandType.BOOLEAN, description = "true if the zone is an edge zone, false otherwise", since = "4.18.0") + private Boolean isEdge; + + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -153,6 +157,13 @@ public class CreateZoneCmd extends BaseCmd { return localStorageEnabled; } + public boolean isEdge() { + if (isEdge == null) { + return false; + } + return isEdge; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// @Override 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 f4728f946f6..b8824fd66f8 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 @@ -141,6 +141,10 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso @Param(description = "The maximum value the MTU can have on the VR's public interfaces", since = "4.18.0") private Integer routerPublicInterfaceMaxMtu; + @SerializedName(ApiConstants.TYPE) + @Param(description = "the type of the zone - core or edge", since = "4.18.0") + String type; + public ZoneResponse() { tags = new LinkedHashSet(); } @@ -352,4 +356,12 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso public void setRouterPublicInterfaceMaxMtu(Integer routerPublicInterfaceMaxMtu) { this.routerPublicInterfaceMaxMtu = routerPublicInterfaceMaxMtu; } + + public void setType(String type) { + this.type = type; + } + + public String getType() { + return type; + } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmdTest.java new file mode 100644 index 00000000000..1e6e7103fbf --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmdTest.java @@ -0,0 +1,35 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.zone; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +public class CreateZoneCmdTest { + + @Test + public void isEdge() { + CreateZoneCmd createZoneCmd = new CreateZoneCmd(); + ReflectionTestUtils.setField(createZoneCmd, "isEdge", null); + Assert.assertFalse("Null or no isedge param value for API should return false", createZoneCmd.isEdge()); + ReflectionTestUtils.setField(createZoneCmd, "isEdge", false); + Assert.assertFalse("false value for isedge param value for API should return false", createZoneCmd.isEdge()); + ReflectionTestUtils.setField(createZoneCmd, "isEdge", true); + Assert.assertTrue("true value for isedge param value for API should return true", createZoneCmd.isEdge()); + } +} \ No newline at end of file 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 0442cac86c8..cff225541d6 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 @@ -127,7 +127,7 @@ public interface ConfigurationManager { * * @param userId * @param podName - * @param zoneId + * @param zone * @param gateway * @param cidr * @param startIp @@ -137,7 +137,7 @@ public interface ConfigurationManager { * (true if it is ok to not validate that gateway IP address overlap with Start/End IP of the POD) * @return Pod */ - HostPodVO createPod(long userId, String podName, long zoneId, String gateway, String cidr, String startIp, String endIp, String allocationState, + HostPodVO createPod(long userId, String podName, DataCenter zone, String gateway, String cidr, String startIp, String endIp, String allocationState, boolean skipGatewayOverlapCheck); /** @@ -164,7 +164,7 @@ public interface ConfigurationManager { */ DataCenterVO createZone(long userId, String zoneName, String dns1, String dns2, String internalDns1, String internalDns2, String guestCidr, String domain, Long domainId, NetworkType zoneType, String allocationState, String networkDomain, boolean isSecurityGroupEnabled, boolean isLocalStorageEnabled, String ip6Dns1, - String ip6Dns2); + String ip6Dns2, boolean isEdge); /** * Deletes a VLAN from the database, along with all of its IP addresses. Will not delete VLANs that have allocated diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineDataCenterVO.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineDataCenterVO.java index 26e982013c2..57382530f40 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineDataCenterVO.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineDataCenterVO.java @@ -37,6 +37,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 com.cloud.dc.DataCenter; import com.cloud.network.Network.Provider; import com.cloud.org.Grouping; import com.cloud.utils.NumbersUtil; @@ -156,6 +157,10 @@ public class EngineDataCenterVO implements EngineDataCenter, Identity { @Temporal(value = TemporalType.TIMESTAMP) protected Date lastUpdated; + @Column(name = "type") + @Enumerated(value = EnumType.STRING) + private DataCenter.Type type; + /** * Note that state is intentionally missing the setter. Any updates to * the state machine needs to go through the DAO object because someone @@ -513,4 +518,9 @@ public class EngineDataCenterVO implements EngineDataCenter, Identity { public PartitionType partitionType() { return PartitionType.Zone; } + + @Override + public DataCenter.Type getType() { + return type; + } } diff --git a/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java b/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java index 038daeaa10b..827b72b58b0 100644 --- a/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java @@ -138,6 +138,10 @@ public class DataCenterVO implements DataCenter { @Column(name = "sort_key") int sortKey; + @Column(name = "type") + @Enumerated(value = EnumType.STRING) + private DataCenter.Type type; + @Override public String getDnsProvider() { return dnsProvider; @@ -472,6 +476,15 @@ public class DataCenterVO implements DataCenter { return PartitionType.Zone; } + @Override + public DataCenter.Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + @Override public String toString() { return String.format("Zone {\"id\": \"%s\", \"name\": \"%s\", \"uuid\": \"%s\"}", id, name, uuid); diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java index a6cd59f1cc3..0754bbf3591 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java @@ -104,6 +104,8 @@ public interface DataCenterDao extends GenericDao { List listEnabledZones(); + List listEnabledNonEdgeZoneIds(); + DataCenterVO findByToken(String zoneToken); DataCenterVO findByTokenOrIdOrName(String tokenIdOrName); diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java index 385fb406155..3bad5ee7eef 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java @@ -20,14 +20,17 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; import javax.persistence.TableGenerator; +import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; +import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterDetailVO; import com.cloud.dc.DataCenterIpAddressVO; import com.cloud.dc.DataCenterLinkLocalIpAddressVO; @@ -63,6 +66,7 @@ public class DataCenterDaoImpl extends GenericDaoBase implem protected SearchBuilder ChildZonesSearch; protected SearchBuilder DisabledZonesSearch; protected SearchBuilder TokenSearch; + protected SearchBuilder ZoneAllocationAndNotTypeSearch; @Inject protected DataCenterIpAddressDao _ipAllocDao = null; @@ -336,6 +340,11 @@ public class DataCenterDaoImpl extends GenericDaoBase implem DisabledZonesSearch.and("allocationState", DisabledZonesSearch.entity().getAllocationState(), SearchCriteria.Op.EQ); DisabledZonesSearch.done(); + ZoneAllocationAndNotTypeSearch = createSearchBuilder(); + ZoneAllocationAndNotTypeSearch.and("allocationState", ZoneAllocationAndNotTypeSearch.entity().getAllocationState(), SearchCriteria.Op.EQ); + ZoneAllocationAndNotTypeSearch.and("type", ZoneAllocationAndNotTypeSearch.entity().getType(), SearchCriteria.Op.NLIKE); + ZoneAllocationAndNotTypeSearch.done(); + TokenSearch = createSearchBuilder(); TokenSearch.and("zoneToken", TokenSearch.entity().getZoneToken(), SearchCriteria.Op.EQ); TokenSearch.done(); @@ -399,6 +408,18 @@ public class DataCenterDaoImpl extends GenericDaoBase implem return dcs; } + @Override + public List listEnabledNonEdgeZoneIds() { + SearchCriteria sc = ZoneAllocationAndNotTypeSearch.create(); + sc.setParameters("allocationState", Grouping.AllocationState.Enabled); + sc.setParameters("type", DataCenter.Type.Edge); + List zones = listBy(sc); + if (CollectionUtils.isEmpty(zones)) { + return new ArrayList<>(); + } + return zones.stream().map(DataCenterVO::getId).collect(Collectors.toList()); + } + @Override public DataCenterVO findByTokenOrIdOrName(String tokenOrIdOrName) { DataCenterVO result = findByToken(tokenOrIdOrName); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql b/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql index cc400143fb2..056a47ba512 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41720to41800.sql @@ -39,6 +39,50 @@ UPDATE `cloud`.`networks` SET public_mtu = 1500, private_mtu = 1500 WHERE remove UPDATE `cloud`.`vpc` SET public_mtu = 1500 WHERE removed IS NULL; UPDATE `cloud`.`nics` SET mtu = 1500 WHERE vm_type='DomainRouter' AND removed IS NULL AND reserver_name IN ('PublicNetworkGuru', 'ExternalGuestNetworkGuru'); +-- Add type column to data_center table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.data_center', 'type', 'varchar(32) DEFAULT ''Core'' COMMENT ''the type of the zone'' '); + +-- Recreate data_center_view +DROP VIEW IF EXISTS `cloud`.`data_center_view`; +CREATE VIEW `cloud`.`data_center_view` AS + select + data_center.id, + data_center.uuid, + data_center.name, + data_center.is_security_group_enabled, + data_center.is_local_storage_enabled, + data_center.description, + data_center.dns1, + data_center.dns2, + data_center.ip6_dns1, + data_center.ip6_dns2, + data_center.internal_dns1, + data_center.internal_dns2, + data_center.guest_network_cidr, + data_center.domain, + data_center.networktype, + data_center.allocation_state, + data_center.zone_token, + data_center.dhcp_provider, + data_center.type, + data_center.removed, + data_center.sort_key, + domain.id domain_id, + domain.uuid domain_uuid, + domain.name domain_name, + domain.path domain_path, + dedicated_resources.affinity_group_id, + dedicated_resources.account_id, + affinity_group.uuid affinity_group_uuid + from + `cloud`.`data_center` + left join + `cloud`.`domain` ON data_center.domain_id = domain.id + left join + `cloud`.`dedicated_resources` ON data_center.id = dedicated_resources.data_center_id + left join + `cloud`.`affinity_group` ON dedicated_resources.affinity_group_id = affinity_group.id; + DROP VIEW IF EXISTS `cloud`.`domain_router_view`; CREATE VIEW `cloud`.`domain_router_view` AS select @@ -65,7 +109,7 @@ CREATE VIEW `cloud`.`domain_router_view` AS data_center.id data_center_id, data_center.uuid data_center_uuid, data_center.name data_center_name, - data_center.networktype data_center_type, + data_center.networktype data_center_network_type, data_center.dns1 dns1, data_center.dns2 dns2, data_center.ip6_dns1 ip6_dns1, @@ -751,7 +795,7 @@ SELECT `data_center`.`uuid` AS `data_center_uuid`, `data_center`.`name` AS `data_center_name`, `data_center`.`is_security_group_enabled` AS `security_group_enabled`, - `data_center`.`networktype` AS `data_center_type`, + `data_center`.`networktype` AS `data_center_network_type`, `host`.`id` AS `host_id`, `host`.`uuid` AS `host_uuid`, `host`.`name` AS `host_name`, diff --git a/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/MockAgentManagerImpl.java b/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/MockAgentManagerImpl.java index 7af282724b2..e7902ee99c2 100644 --- a/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/MockAgentManagerImpl.java +++ b/plugins/hypervisors/simulator/src/main/java/com/cloud/agent/manager/MockAgentManagerImpl.java @@ -28,6 +28,9 @@ import com.cloud.agent.api.MaintainAnswer; import com.cloud.agent.api.PingTestCommand; import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.api.commands.SimulatorAddSecondaryAgent; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.exception.DiscoveryException; import com.cloud.host.HostVO; @@ -79,6 +82,8 @@ import java.util.regex.PatternSyntaxException; public class MockAgentManagerImpl extends ManagerBase implements MockAgentManager { private static final Logger s_logger = Logger.getLogger(MockAgentManagerImpl.class); @Inject + DataCenterDao dcDao; + @Inject HostPodDao _podDao = null; @Inject MockHostDao _mockHostDao = null; @@ -106,7 +111,12 @@ public class MockAgentManagerImpl extends ManagerBase implements MockAgentManage private Pair getPodCidr(long podId, long dcId) { try { - + DataCenterVO zone = dcDao.findById(dcId); + if (DataCenter.Type.Edge.equals(zone.getType())) { + String subnet = String.format("172.%d.%d.0", random.nextInt(15) + 16, random.nextInt(6) + 1); + s_logger.info(String.format("Pod belongs to an edge zone hence CIDR cannot be found, returning %s/24", subnet)); + return new Pair<>(subnet, 24L); + } HashMap> podMap = _podDao.getCurrentPodCidrSubnets(dcId, 0); List cidrPair = podMap.get(podId); String cidrAddress = (String)cidrPair.get(0); diff --git a/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/ManagementServerMock.java b/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/ManagementServerMock.java index afbb7355fd6..99d46d57f67 100644 --- a/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/ManagementServerMock.java +++ b/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/ManagementServerMock.java @@ -387,7 +387,7 @@ public class ManagementServerMock { ConfigurationManager mgr = (ConfigurationManager)_configService; _zone = mgr.createZone(User.UID_SYSTEM, "default", "8.8.8.8", null, "8.8.4.4", null, null /* cidr */, "ROOT", Domain.ROOT_DOMAIN, NetworkType.Advanced, null, - null /* networkDomain */, false, false, null, null); + null /* networkDomain */, false, false, null, null, false); } } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 437ff05d28d..e12e612fe51 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -37,11 +37,6 @@ import java.util.stream.Collectors; import javax.inject.Inject; -import com.cloud.host.ControlState; -import com.cloud.utils.security.CertificateHelper; -import com.cloud.user.UserData; -import com.cloud.api.query.dao.UserVmJoinDao; -import com.cloud.network.vpc.VpcVO; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroup; @@ -205,6 +200,7 @@ import org.apache.log4j.Logger; import com.cloud.agent.api.VgpuTypesInfo; import com.cloud.api.query.ViewResponseHelper; +import com.cloud.api.query.dao.UserVmJoinDao; import com.cloud.api.query.vo.AccountJoinVO; import com.cloud.api.query.vo.AsyncJobJoinVO; import com.cloud.api.query.vo.ControlledViewEntity; @@ -254,6 +250,7 @@ import com.cloud.event.Event; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.gpu.GPU; +import com.cloud.host.ControlState; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.hypervisor.HypervisorCapabilities; @@ -318,6 +315,7 @@ import com.cloud.network.vpc.PrivateGateway; import com.cloud.network.vpc.StaticRoute; import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.VpcOffering; +import com.cloud.network.vpc.VpcVO; import com.cloud.offering.DiskOffering; import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Detail; @@ -361,6 +359,7 @@ import com.cloud.user.AccountManager; import com.cloud.user.SSHKeyPair; import com.cloud.user.User; import com.cloud.user.UserAccount; +import com.cloud.user.UserData; import com.cloud.user.UserStatisticsVO; import com.cloud.user.dao.UserStatisticsDao; import com.cloud.uservm.UserVm; @@ -374,6 +373,7 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.Dhcp; import com.cloud.utils.net.Ip; import com.cloud.utils.net.NetUtils; +import com.cloud.utils.security.CertificateHelper; import com.cloud.vm.ConsoleProxyVO; import com.cloud.vm.InstanceGroup; import com.cloud.vm.Nic; 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 20a139f469f..dba2363d4c9 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -183,6 +183,7 @@ import com.cloud.api.query.vo.TemplateJoinVO; import com.cloud.api.query.vo.UserAccountJoinVO; import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.api.query.vo.VolumeJoinVO; +import com.cloud.dc.DataCenter; import com.cloud.dc.DedicatedResourceVO; import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.domain.Domain; @@ -3034,6 +3035,10 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q SearchCriteria zoneSC = sb.create(); zoneSC.setParameters("zoneId", String.valueOf(zoneId)); sc.addAnd("zoneId", SearchCriteria.Op.SC, zoneSC); + DataCenterJoinVO zone = _dcJoinDao.findById(zoneId); + if (DataCenter.Type.Edge.equals(zone.getType())) { + sc.addAnd("useLocalStorage", Op.EQ, true); + } } DiskOffering currentDiskOffering = null; @@ -3281,6 +3286,10 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q SearchCriteria zoneSC = sb.create(); zoneSC.setParameters("zoneId", String.valueOf(zoneId)); sc.addAnd("zoneId", SearchCriteria.Op.SC, zoneSC); + DataCenterJoinVO zone = _dcJoinDao.findById(zoneId); + if (DataCenter.Type.Edge.equals(zone.getType())) { + sc.addAnd("useLocalStorage", Op.EQ, true); + } } if (cpuNumber != null) { 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 899cf6d7a5d..50c5275390e 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 @@ -27,6 +27,7 @@ import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.ResourceTagResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.commons.lang3.ObjectUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -69,6 +70,7 @@ public class DataCenterJoinDaoImpl extends GenericDaoBase() { - @Override - public HostPodVO doInTransaction(final TransactionStatus status) { + return Transaction.execute((TransactionCallback) status -> { - final HostPodVO pod = _podDao.persist(podFinal); + final HostPodVO pod = _podDao.persist(podFinal); - if (StringUtils.isNotEmpty(startIp)) { - _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, endIpFinal, false, null); - } - - final String[] linkLocalIpRanges = NetUtils.getLinkLocalIPRange(_configDao.getValue(Config.ControlCidr.key())); - if (linkLocalIpRanges != null) { - _zoneDao.addLinkLocalIpAddress(zoneId, pod.getId(), linkLocalIpRanges[0], linkLocalIpRanges[1]); - } - - CallContext.current().putContextParameter(Pod.class, pod.getUuid()); - - return pod; + if (StringUtils.isNotEmpty(startIpFinal)) { + _zoneDao.addPrivateIpAddress(zone.getId(), pod.getId(), startIpFinal, endIpFinal, false, null); } + + final String[] linkLocalIpRanges = NetUtils.getLinkLocalIPRange(_configDao.getValue(Config.ControlCidr.key())); + if (linkLocalIpRanges.length > 1) { + _zoneDao.addLinkLocalIpAddress(zone.getId(), pod.getId(), linkLocalIpRanges[0], linkLocalIpRanges[1]); + } + + CallContext.current().putContextParameter(Pod.class, pod.getUuid()); + + return pod; }); } @@ -2624,7 +2642,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (allocationStateStr != null && !allocationStateStr.isEmpty()) { final Grouping.AllocationState allocationState = Grouping.AllocationState.valueOf(allocationStateStr); - if (allocationState == Grouping.AllocationState.Enabled) { + if (allocationState == Grouping.AllocationState.Enabled && !DataCenter.Type.Edge.equals(zone.getType())) { // check if zone has necessary trafficTypes before enabling try { PhysicalNetwork mgmtPhyNetwork; @@ -2694,7 +2712,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @DB public DataCenterVO createZone(final long userId, final String zoneName, final String dns1, final String dns2, final String internalDns1, final String internalDns2, final String guestCidr, final String domain, final Long domainId, final NetworkType zoneType, final String allocationStateStr, final String networkDomain, final boolean isSecurityGroupEnabled, final boolean isLocalStorageEnabled, - final String ip6Dns1, final String ip6Dns2) { + final String ip6Dns1, final String ip6Dns2, final boolean isEdge) { // checking the following params outside checkzoneparams method as we do // not use these params for updatezone @@ -2728,6 +2746,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // physical network and providers setup. zoneFinal.setAllocationState(Grouping.AllocationState.Disabled); } + zoneFinal.setType(isEdge ? DataCenter.Type.Edge : DataCenter.Type.Core); return Transaction.execute(new TransactionCallback() { @Override @@ -2838,6 +2857,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati final String networkDomain = cmd.getDomain(); boolean isSecurityGroupEnabled = cmd.getSecuritygroupenabled(); final boolean isLocalStorageEnabled = cmd.getLocalStorageEnabled(); + final boolean isEdge = cmd.isEdge(); if (allocationState == null) { allocationState = Grouping.AllocationState.Disabled.toString(); @@ -2856,6 +2876,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("guestCidrAddress parameter is not supported for Basic zone"); } + if (!NetworkType.Advanced.equals(zoneType) && isEdge) { + throw new InvalidParameterValueException("Only advanced network type zones can be edge zones"); + } + DomainVO domainVO = null; if (domainId != null) { @@ -2867,7 +2891,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } return createZone(userId, zoneName, dns1, dns2, internalDns1, internalDns2, guestCidr, domainVO != null ? domainVO.getName() : null, domainId, zoneType, allocationState, - networkDomain, isSecurityGroupEnabled, isLocalStorageEnabled, ip6Dns1, ip6Dns2); + networkDomain, isSecurityGroupEnabled, isLocalStorageEnabled, ip6Dns1, ip6Dns2, isEdge); } @Override @@ -5711,10 +5735,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } private boolean validPod(final String podName, final long zoneId) { - if (!validZone(zoneId)) { - return false; - } - return _podDao.findByName(podName, zoneId) != null; } diff --git a/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index e0c48956a63..c1d4a22bf77 100644 --- a/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -21,15 +21,17 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.utils.PasswordGenerator; import org.apache.cloudstack.agent.lb.IndirectAgentLB; import org.apache.cloudstack.ca.CAManager; import org.apache.cloudstack.consoleproxy.ConsoleAccessManager; @@ -48,6 +50,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -117,6 +120,7 @@ import com.cloud.user.AccountManager; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; +import com.cloud.utils.PasswordGenerator; import com.cloud.utils.StringUtils; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; @@ -148,10 +152,6 @@ import com.cloud.vm.dao.VMInstanceDao; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonParseException; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import org.apache.commons.lang3.BooleanUtils; /** * Class to manage console proxys.

@@ -1491,15 +1491,11 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy @Override public Long[] getScannablePools() { - List zones = dataCenterDao.listEnabledZones(); - - Long[] dcIdList = new Long[zones.size()]; - int i = 0; - for (DataCenterVO dc : zones) { - dcIdList[i++] = dc.getId(); + List zoneIds = dataCenterDao.listEnabledNonEdgeZoneIds(); + if (s_logger.isDebugEnabled()) { + s_logger.debug(String.format("Enabled non-edge zones available for scan: %s", org.apache.commons.lang3.StringUtils.join(zoneIds, ","))); } - - return dcIdList; + return zoneIds.toArray(Long[]::new); } @Override 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 2cd377a04f9..24581aa1ee7 100644 --- a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -3320,6 +3320,11 @@ Configurable, StateListener[] getConfigKeys() { return new ConfigKey[] { + RouterTemplateKvm, + RouterTemplateVmware, + RouterTemplateHyperV, + RouterTemplateLxc, + RouterTemplateOvm3, UseExternalDnsServers, RouterVersionCheckEnabled, SetServiceMonitor, diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 6b66b999ead..bd8811b2a15 100755 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -78,7 +78,10 @@ import com.cloud.api.query.MutualExclusiveIdsManagerBase; import com.cloud.configuration.Config; import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.DataCenterDao; import com.cloud.domain.dao.DomainDao; import com.cloud.event.ActionEvent; import com.cloud.event.ActionEventUtils; @@ -216,6 +219,8 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement @Inject protected SnapshotHelper snapshotHelper; + @Inject + DataCenterDao dataCenterDao; private int _totalRetries; private int _pauseInterval; @@ -223,6 +228,14 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement private ScheduledExecutorService backupSnapshotExecutor; + protected boolean isBackupSnapshotToSecondaryForZone(long zoneId) { + if (Boolean.FALSE.equals(SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value())) { + return false; + } + DataCenterVO zone = dataCenterDao.findById(zoneId); + return !DataCenter.Type.Edge.equals(zone.getType()); + } + @Override public String getConfigComponentName() { return SnapshotManager.class.getSimpleName(); @@ -1250,7 +1263,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement } SnapshotInfo snapshotOnPrimary = snapshotStrategy.takeSnapshot(snapshot); - boolean backupSnapToSecondary = SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value() == null || SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value(); + boolean backupSnapToSecondary = isBackupSnapshotToSecondaryForZone(snapshot.getDataCenterId()); if (backupSnapToSecondary) { backupSnapshotToSecondary(payload.getAsyncBackup(), snapshotStrategy, snapshotOnPrimary); @@ -1262,7 +1275,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement try { postCreateSnapshot(volume.getId(), snapshotId, payload.getSnapshotPolicyId()); - DataStoreRole dataStoreRole = snapshotHelper.getDataStoreRole(snapshot); + DataStoreRole dataStoreRole = backupSnapToSecondary ? snapshotHelper.getDataStoreRole(snapshot) : DataStoreRole.Primary; SnapshotDataStoreVO snapshotStoreRef = _snapshotStoreDao.findBySnapshot(snapshotId, dataStoreRole); if (snapshotStoreRef == null) { diff --git a/server/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManagerImpl.java b/server/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManagerImpl.java index fb80983415d..0f5fdca5381 100644 --- a/server/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManagerImpl.java @@ -16,11 +16,33 @@ // under the License. package org.apache.cloudstack.consoleproxy; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.api.command.user.consoleproxy.ConsoleEndpoint; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.security.keys.KeysManager; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; + import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.GetVmVncTicketAnswer; import com.cloud.agent.api.GetVmVncTicketCommand; import com.cloud.consoleproxy.ConsoleProxyManager; +import com.cloud.dc.DataCenter; +import com.cloud.dc.dao.DataCenterDao; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.PermissionDeniedException; @@ -48,25 +70,6 @@ import com.cloud.vm.dao.ConsoleSessionDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import org.apache.cloudstack.api.command.user.consoleproxy.ConsoleEndpoint; -import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.framework.security.keys.KeysManager; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import javax.inject.Inject; -import javax.naming.ConfigurationException; - -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.UUID; public class ConsoleAccessManagerImpl extends ManagerBase implements ConsoleAccessManager { @@ -87,6 +90,8 @@ public class ConsoleAccessManagerImpl extends ManagerBase implements ConsoleAcce @Inject private ConsoleProxyManager consoleProxyManager; @Inject + DataCenterDao dataCenterDao; + @Inject private ConsoleSessionDao consoleSessionDao; private static KeysManager secretKeysManager; @@ -135,6 +140,13 @@ public class ConsoleAccessManagerImpl extends ManagerBase implements ConsoleAcce return new ConsoleEndpoint(false, null, "Permission denied"); } + DataCenter zone = dataCenterDao.findById(vm.getDataCenterId()); + if (zone != null && DataCenter.Type.Edge.equals(zone.getType())) { + String errorMsg = "Console access is not supported for Edge zones"; + s_logger.error(errorMsg); + return new ConsoleEndpoint(false, null, errorMsg); + } + String sessionUuid = UUID.randomUUID().toString(); return generateAccessEndpoint(vmId, sessionUuid, extraSecurityToken, clientAddress); } catch (Exception e) { diff --git a/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java b/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java index 7515f125972..05e38f66da9 100644 --- a/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java +++ b/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java @@ -46,6 +46,8 @@ import org.apache.cloudstack.api.command.admin.network.ListGuestNetworkIpv6Prefi import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; +import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; +import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; import org.apache.cloudstack.api.command.user.network.ListNetworkOfferingsCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; @@ -66,12 +68,14 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.mockito.stubbing.Answer; +import org.springframework.test.util.ReflectionTestUtils; import com.cloud.api.query.dao.NetworkOfferingJoinDao; import com.cloud.api.query.vo.NetworkOfferingJoinVO; import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.AccountVlanMapVO; import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterGuestIpv6Prefix; import com.cloud.dc.DataCenterGuestIpv6PrefixVO; @@ -106,10 +110,10 @@ import com.cloud.network.dao.Ipv6GuestPrefixSubnetNetworkMapDao; import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.projects.ProjectManager; -import com.cloud.storage.dao.DiskOfferingDao; -import com.cloud.storage.dao.StoragePoolTagsDao; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.StoragePoolTagsDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; @@ -1230,4 +1234,95 @@ public class ConfigurationManagerTest { throw new RuntimeException(e); } } + + private void mockPersistDatacenterForCreateZone() { + Mockito.when(_zoneDao.persist(Mockito.any(DataCenterVO.class))).thenAnswer((Answer) invocation -> { + DataCenterVO zone = (DataCenterVO)invocation.getArguments()[0]; + ReflectionTestUtils.setField(zone, "uuid", UUID.randomUUID().toString()); + ReflectionTestUtils.setField(zone, "id", 1L); + return zone; + }); + } + + @Test + public void testCreateEdgeZone() { + CreateZoneCmd cmd = Mockito.mock(CreateZoneCmd.class); + Mockito.when(cmd.isEdge()).thenReturn(true); + Mockito.when(cmd.getNetworkType()).thenReturn(NetworkType.Advanced.toString()); + Mockito.when(cmd.getDomainId()).thenReturn(null); + mockPersistDatacenterForCreateZone(); + DataCenter zone = configurationMgr.createZone(cmd); + Assert.assertNotNull(zone); + Assert.assertEquals(NetworkType.Advanced, zone.getNetworkType()); + Assert.assertEquals(DataCenter.Type.Edge, zone.getType()); + } + + @Test + public void testCreateCoreZone() { + CreateZoneCmd cmd = Mockito.mock(CreateZoneCmd.class); + Mockito.when(cmd.isEdge()).thenReturn(false); + Mockito.when(cmd.getNetworkType()).thenReturn(NetworkType.Advanced.toString()); + Mockito.when(cmd.getDomainId()).thenReturn(null); + mockPersistDatacenterForCreateZone(); + DataCenter zone = configurationMgr.createZone(cmd); + Assert.assertNotNull(zone); + Assert.assertEquals(NetworkType.Advanced, zone.getNetworkType()); + Assert.assertEquals(DataCenter.Type.Core, zone.getType()); + } + + @Test + public void testCreateBasicZone() { + CreateZoneCmd cmd = Mockito.mock(CreateZoneCmd.class); + Mockito.when(cmd.isEdge()).thenReturn(false); + Mockito.when(cmd.getNetworkType()).thenReturn(NetworkType.Basic.toString()); + Mockito.when(cmd.getDomainId()).thenReturn(null); + mockPersistDatacenterForCreateZone(); + DataCenter zone = configurationMgr.createZone(cmd); + Assert.assertNotNull(zone); + Assert.assertEquals(NetworkType.Basic, zone.getNetworkType()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testCreateBasicEdgeZoneFailure() { + CreateZoneCmd cmd = Mockito.mock(CreateZoneCmd.class); + Mockito.when(cmd.isEdge()).thenReturn(true); + Mockito.when(cmd.getNetworkType()).thenReturn(NetworkType.Basic.toString()); + Mockito.when(cmd.getDomainId()).thenReturn(null); + configurationMgr.createZone(cmd); + } + + @Test + public void testEditEdgeZone() { + // editZone should be successful despite no Public network + final Long zoneId = 1L; + UpdateZoneCmd cmd = Mockito.mock(UpdateZoneCmd.class); + Mockito.when(cmd.getId()).thenReturn(zoneId); + Mockito.when(cmd.getZoneName()).thenReturn("NewName"); + DataCenterVO zone = Mockito.mock(DataCenterVO.class); + Mockito.when(zone.getNetworkType()).thenReturn(NetworkType.Advanced); + Mockito.when(zone.getType()).thenReturn(DataCenter.Type.Edge); + Mockito.when(zone.getId()).thenReturn(zoneId); + Mockito.when(_zoneDao.findById(Mockito.anyLong())).thenReturn(zone); + Mockito.when(_networkModel.getDefaultPhysicalNetworkByZoneAndTrafficType(zoneId, Networks.TrafficType.Public)).thenReturn(null); + Mockito.when(_zoneDao.update(Mockito.anyLong(), Mockito.any(DataCenterVO.class))).thenReturn(true); + configurationMgr.editZone(cmd); + } + + @Test + public void testEdgeZoneCreatePod() { + final long zoneId = 1L; + DataCenterVO zone = Mockito.mock(DataCenterVO.class); + Mockito.when(zone.getNetworkType()).thenReturn(NetworkType.Advanced); + Mockito.when(zone.getType()).thenReturn(DataCenter.Type.Edge); + Mockito.when(zone.getId()).thenReturn(1L); + Mockito.when(_zoneDao.findById(Mockito.anyLong())).thenReturn(zone); + Mockito.when(_configDao.getValue(Config.ControlCidr.key())).thenReturn(Config.ControlCidr.getDefaultValue()); + Mockito.when(_podDao.persist(Mockito.any(HostPodVO.class))).thenAnswer((Answer) invocation -> { + HostPodVO pod = (HostPodVO)invocation.getArguments()[0]; + ReflectionTestUtils.setField(pod, "uuid", UUID.randomUUID().toString()); + ReflectionTestUtils.setField(pod, "id", 1L); + return pod; + }); + configurationMgr.createPod(zoneId, "TestPod", null, null, null, null, null); + } } diff --git a/server/src/test/java/com/cloud/consoleproxy/ConsoleProxyManagerTest.java b/server/src/test/java/com/cloud/consoleproxy/ConsoleProxyManagerTest.java index 5dc7df807d2..3a50c5c4685 100644 --- a/server/src/test/java/com/cloud/consoleproxy/ConsoleProxyManagerTest.java +++ b/server/src/test/java/com/cloud/consoleproxy/ConsoleProxyManagerTest.java @@ -24,7 +24,10 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.List; import org.apache.log4j.Logger; import org.junit.Assert; @@ -275,4 +278,24 @@ public class ConsoleProxyManagerTest { ConsoleProxyStatus result = new ConsoleProxyManagerImpl().parseJsonToConsoleProxyStatus(null); Assert.assertEquals(expectedResult, result); } + + private void verifyScannablePoolsZoneIds(List expected, Long[] result) { + Assert.assertNotNull(result); + Assert.assertEquals(expected.size(), result.length); + for (int i = 0; i < expected.size(); ++i) { + Assert.assertEquals(expected.get(i), result[i]); + } + } + + @Test + public void testGetScannablePools() { + List dbZoneIds = new ArrayList<>(); + Mockito.when(dataCenterDaoMock.listEnabledNonEdgeZoneIds()).thenReturn(dbZoneIds); + ConsoleProxyManagerImpl consoleProxyManager = new ConsoleProxyManagerImpl(); + ReflectionTestUtils.setField(consoleProxyManager, "dataCenterDao", dataCenterDaoMock); + verifyScannablePoolsZoneIds(dbZoneIds, consoleProxyManager.getScannablePools()); + dbZoneIds = Arrays.asList(2L, 3L); + Mockito.when(dataCenterDaoMock.listEnabledNonEdgeZoneIds()).thenReturn(dbZoneIds); + verifyScannablePoolsZoneIds(dbZoneIds, consoleProxyManager.getScannablePools()); + } } diff --git a/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerTest.java b/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerTest.java index f17d300596a..8f623d5a433 100755 --- a/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerTest.java +++ b/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerTest.java @@ -24,7 +24,11 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import org.apache.cloudstack.acl.ControlledEntity; @@ -37,10 +41,11 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy.SnapshotOperation; -import org.apache.cloudstack.snapshot.SnapshotHelper; import org.apache.cloudstack.engine.subsystem.api.storage.StorageStrategyFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.snapshot.SnapshotHelper; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; @@ -49,12 +54,21 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.BDDMockito; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; +import org.mockito.verification.VerificationMode; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; import com.cloud.configuration.Resource.ResourceType; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.hypervisor.Hypervisor; @@ -88,15 +102,6 @@ import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.snapshot.VMSnapshot; import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import org.junit.runner.RunWith; -import org.mockito.BDDMockito; -import org.mockito.verification.VerificationMode; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest(GlobalLock.class) @@ -171,6 +176,8 @@ public class SnapshotManagerTest { @Mock TaggedResourceService taggedResourceServiceMock; + @Mock + DataCenterDao dataCenterDao; SnapshotPolicyVO snapshotPolicyVoInstance; @@ -207,6 +214,7 @@ public class SnapshotManagerTest { _snapshotMgr._snapshotPolicyDao = snapshotPolicyDaoMock; _snapshotMgr._snapSchedMgr = snapshotSchedulerMock; _snapshotMgr.taggedResourceService = taggedResourceServiceMock; + _snapshotMgr.dataCenterDao = dataCenterDao; when(_snapshotDao.findById(anyLong())).thenReturn(snapshotMock); when(snapshotMock.getVolumeId()).thenReturn(TEST_VOLUME_ID); @@ -533,4 +541,38 @@ public class SnapshotManagerTest { Mockito.any(DateUtil.IntervalType.class), Mockito.anyInt(), Mockito.anyBoolean(), Mockito.anyBoolean()); Mockito.verify(_snapshotMgr, timesVerification).createTagsForSnapshotPolicy(Mockito.any(), Mockito.any()); } + + private void mockForBackupSnapshotToSecondaryZoneTest(final Boolean configValue, final DataCenter.Type dcType) { + try { + Field f = ConfigKey.class.getDeclaredField("_value"); + f.setAccessible(true); + f.set(SnapshotInfo.BackupSnapshotAfterTakingSnapshot, configValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + Assert.fail(String.format("Failed to override config: %s", e.getMessage())); + } + DataCenterVO dc = Mockito.mock(DataCenterVO.class); + Mockito.when(dc.getType()).thenReturn(dcType); + Mockito.when(dataCenterDao.findById(1L)).thenReturn(dc); + } + @Test + public void testIsBackupSnapshotToSecondaryForCoreZoneNullConfig() { + mockForBackupSnapshotToSecondaryZoneTest(null, DataCenter.Type.Core); + Assert.assertTrue(_snapshotMgr.isBackupSnapshotToSecondaryForZone(1L)); + } + @Test + public void testIsBackupSnapshotToSecondaryForCoreZoneEnabledConfig() { + mockForBackupSnapshotToSecondaryZoneTest(true, DataCenter.Type.Core); + Assert.assertTrue(_snapshotMgr.isBackupSnapshotToSecondaryForZone(1L)); + } + @Test + public void testIsBackupSnapshotToSecondaryForCoreZoneDisabledConfig() { + mockForBackupSnapshotToSecondaryZoneTest(false, DataCenter.Type.Core); + Assert.assertFalse(_snapshotMgr.isBackupSnapshotToSecondaryForZone(1L)); + } + + @Test + public void testIsBackupSnapshotToSecondaryForEdgeZone() { + mockForBackupSnapshotToSecondaryZoneTest(true, DataCenter.Type.Edge); + Assert.assertFalse(_snapshotMgr.isBackupSnapshotToSecondaryForZone(1L)); + } } diff --git a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java index 088f4e67e8f..56f2428dd55 100644 --- a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -506,7 +506,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu * @see com.cloud.configuration.ConfigurationManager#createPod(long, java.lang.String, long, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean) */ @Override - public HostPodVO createPod(long userId, String podName, long zoneId, String gateway, String cidr, String startIp, String endIp, String allocationState, + public HostPodVO createPod(long userId, String podName, DataCenter zone, String gateway, String cidr, String startIp, String endIp, String allocationState, boolean skipGatewayOverlapCheck) { // TODO Auto-generated method stub return null; @@ -632,7 +632,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu @Override public DataCenterVO createZone(long userId, String zoneName, String dns1, String dns2, String internalDns1, String internalDns2, String guestCidr, String domain, Long domainId, NetworkType zoneType, String allocationState, String networkDomain, boolean isSecurityGroupEnabled, boolean isLocalStorageEnabled, String ip6Dns1, - String ip6Dns2) { + String ip6Dns2, boolean isEdge) { // TODO Auto-generated method stub return null; } diff --git a/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java b/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java index 9c9b4f56324..68ed368483b 100644 --- a/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java +++ b/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java @@ -30,8 +30,6 @@ import java.util.Map; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.storage.VolumeApiService; -import com.cloud.utils.PasswordGenerator; import org.apache.cloudstack.agent.lb.IndirectAgentLB; import org.apache.cloudstack.ca.CAManager; import org.apache.cloudstack.context.CallContext; @@ -50,6 +48,8 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao; import org.apache.cloudstack.utils.identity.ManagementServerNode; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -112,6 +112,7 @@ import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.ImageStoreDetailsUtil; import com.cloud.storage.Storage; import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VolumeApiService; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.UploadDao; @@ -127,6 +128,7 @@ import com.cloud.user.AccountService; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; +import com.cloud.utils.PasswordGenerator; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.QueryBuilder; @@ -150,8 +152,6 @@ import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.SecondaryStorageVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; /** * Class to manage secondary storages.

@@ -1307,15 +1307,11 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar @Override public Long[] getScannablePools() { - List zones = _dcDao.listEnabledZones(); - - Long[] dcIdList = new Long[zones.size()]; - int i = 0; - for (DataCenterVO dc : zones) { - dcIdList[i++] = dc.getId(); + List zoneIds = _dcDao.listEnabledNonEdgeZoneIds(); + if (s_logger.isDebugEnabled()) { + s_logger.debug(String.format("Enabled non-edge zones available for scan: %s", StringUtils.join(zoneIds, ","))); } - - return dcIdList; + return zoneIds.toArray(Long[]::new); } @Override diff --git a/services/secondary-storage/controller/src/test/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerTest.java b/services/secondary-storage/controller/src/test/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerTest.java index 98348fe2908..fe67b24cac2 100644 --- a/services/secondary-storage/controller/src/test/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerTest.java +++ b/services/secondary-storage/controller/src/test/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerTest.java @@ -17,7 +17,17 @@ package org.apache.cloudstack.secondarystorage; +import static org.mockito.AdditionalMatchers.not; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.List; import org.junit.Assert; import org.junit.Before; @@ -27,8 +37,8 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import com.cloud.dc.DataCenterVO; import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.hypervisor.Hypervisor; import com.cloud.network.Networks.TrafficType; @@ -37,16 +47,6 @@ import com.cloud.network.dao.NetworkVO; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.NicProfile; import com.cloud.vm.VirtualMachineProfile; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.when; -import static org.mockito.AdditionalMatchers.not; -import static org.mockito.Mockito.eq; public class SecondaryStorageManagerTest { @Mock @@ -197,7 +197,7 @@ public class SecondaryStorageManagerTest { } @Test - public void validateVerifySshAccessOnManagementNicForSystemVm(){ + public void validateVerifySshAccessOnManagementNicForSystemVm() { Hypervisor.HypervisorType[] hypervisorTypesArray = Hypervisor.HypervisorType.values(); List hypervisorTypesThatMustReturnManagementNic = new ArrayList<>(Arrays.asList(Hypervisor.HypervisorType.Hyperv)); @@ -218,4 +218,22 @@ public class SecondaryStorageManagerTest { Assert.assertEquals(expectedResult, result); } } + + private void verifyScannablePoolsZoneIds(List expected, Long[] result) { + Assert.assertNotNull(result); + Assert.assertEquals(expected.size(), result.length); + for (int i = 0; i < expected.size(); ++i) { + Assert.assertEquals(expected.get(i), result[i]); + } + } + + @Test + public void testGetScannablePools() { + List dbZoneIds = new ArrayList<>(); + Mockito.when(_dcDao.listEnabledNonEdgeZoneIds()).thenReturn(dbZoneIds); + verifyScannablePoolsZoneIds(dbZoneIds, _ssMgr.getScannablePools()); + dbZoneIds = Arrays.asList(1L, 2L, 3L); + Mockito.when(_dcDao.listEnabledNonEdgeZoneIds()).thenReturn(dbZoneIds); + verifyScannablePoolsZoneIds(dbZoneIds, _ssMgr.getScannablePools()); + } } diff --git a/test/integration/component/test_edgezone_supportedoperations.py b/test/integration/component/test_edgezone_supportedoperations.py new file mode 100644 index 00000000000..c3f12686523 --- /dev/null +++ b/test/integration/component/test_edgezone_supportedoperations.py @@ -0,0 +1,133 @@ +# 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. +""" BVT tests for supported VM and network operations on an Edge zone +""" +# Import Local Modules +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.cloudstackAPI import (attachVolume, + detachVolume, + deleteVolume, + attachIso, + detachIso, + deleteIso, + startVirtualMachine, + stopVirtualMachine, + migrateVirtualMachineWithVolume) +from marvin.lib.utils import (cleanup_resources) +from marvin.lib.base import (Account, + Host, + Pod, + StoragePool, + ServiceOffering, + DiskOffering, + VirtualMachine, + Iso, + Volume) +from marvin.lib.common import (get_domain, + get_zone, + get_template) +from marvin.lib.decoratorGenerators import skipTestIf +from marvin.codes import FAILED, PASS +from nose.plugins.attrib import attr +# Import System modules +import time + +_multiprocess_shared_ = True + + +class TestEdgeZoneSupportedOperations(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestVMMigration, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + cls.domain = get_domain(cls.apiclient) + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + cls.services['mode'] = cls.zone.networktype + cls.hypervisor = cls.testClient.getHypervisorInfo() + cls.cleanup = [] + cls.testsNotSupported = False + if cls.zone.type != 'Edge': + cls.testsNotSupported = True + return + cls.services["test_templates"]["kvm"]["directdownload"] = "true" + cls.template = Template.register(cls.apiclient, cls.services["test_templates"]["kvm"], + zoneid=cls.zone.id, hypervisor=cls.hypervisor) + cls.cleanup.append(cls.template) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + cls.services["virtual_machine"]["hypervisor"] = cls.hypervisor + cls.services["service_offerings"]["tiny"]["storagetype"] = "local" + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["tiny"] + ) + cls.cleanup.append(cls.service_offering) + cls.l2_network_offering = NetworkOffering.create( + cls.apiclient, + cls.services["l2-l2_network_offering"], + ) + cls.cleanup.append(cls.l2_network_offering) + cls.l2_network_offering.update(cls.apiclient, state='Enabled') + cls.domain = get_domain(cls.apiclient) + cls.account = Account.create( + cls.apiclient, + cls.services["account"], + admin=False, + domainid=cls.domain.id + ) + cls.cleanup.append(cls.account) + + @classmethod + def tearDownClass(cls): + super(TestMetrics, cls).tearDownClass() + + def setUp(self): + self.userapiclient = self.testClient.getUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain + ) + self.cleanup = [] + + def tearDown(self): + super(TestMetrics, self).tearDown() + + @skipTestIf("testsNotSupported") + @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false") + def test_01_deploy_vm_l2network(self): + """Test to deploy VM in a L2 network + """ + network = Network.create( + self.apiclient, + self.services["l2-network"], + zoneid=self.zone.id, + networkofferingid=self.l2_network_offering.id + ) + self.cleanup.append(network) + vm = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + serviceofferingid=self.service_offering.id + networkids=network.id + ) + self.cleanup.append(vm) + self.assertEqual( + vm.state, + "Running", + "Check VM deployed in edge zone with a L2 network is running" + ) diff --git a/test/integration/component/test_interpod_migration.py b/test/integration/component/test_interpod_migration.py index b8b2e975975..e00e0d7e310 100644 --- a/test/integration/component/test_interpod_migration.py +++ b/test/integration/component/test_interpod_migration.py @@ -14,7 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -""" BVT tests for Virtual Machine Life Cycle +""" BVT tests for Virtual Machine migration across pods """ # Import Local Modules from marvin.cloudstackTestCase import cloudstackTestCase diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index a4d0d0b6197..5f17374e17c 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -451,6 +451,8 @@ "label.copy": "Copy", "label.copy.clipboard": "Copy to clipboard", "label.copyid": "Copy ID", +"label.core": "Core", +"label.core.zone.type": "Core zone type", "label.counter": "Counter", "label.counter.name": "Name of the counter for which the policy will be evaluated", "label.cpu": "CPU", @@ -666,6 +668,15 @@ "label.duration.7days": "7 days", "label.dynamicscalingenabled": "Dynamic scaling enabled", "label.dynamicscalingenabled.tooltip": "VM can dynamically scale only when dynamic scaling is enabled on template, service offering and global setting.", +<<<<<<< HEAD +"label.diskofferingstrictness": "Disk offering strictness", +"label.disksizestrictness": "Disk size strictness", +"label.computeonly.offering": "Compute only disk offering", +"label.computeonly.offering.tooltip": "Option to specify root disk related information in the compute offering or to directly link a disk offering to the compute offering", +"label.edge": "Edge", +"label.edge.zone": "Edge Zone", +======= +>>>>>>> main "label.edit": "Edit", "label.edit.acl.list": "Edit ACL list", "label.edit.acl.rule": "Edit ACL rule", @@ -2260,6 +2271,8 @@ "message.deployasis": "Selected template is Deploy As-Is i.e., the VM is deployed by importing an OVA with vApps directly into vCenter. Root disk(s) resize is allowed only on stopped VMs for such templates.", "message.desc.advanced.zone": "This is recommended and allows more sophisticated network topologies. This network model provides the most flexibility in defining guest networks and providing custom network offerings such as firewall, VPN, or load balancer support.", "message.desc.basic.zone": "Provide a single network where each VM instance is assigned an IP directly from the network. Guest isolation can be provided through layer-3 means such as security groups (IP address source filtering).", +"message.desc.core.zone": "Core Zones are intended for Datacenter based deployments and allow the full range of networking and other functionality in Apache CloudStack. Core zones have a number of prerequisites and rely on the presence of shared storage and helper VMs.", +"message.desc.edge.zone": "Edge Zones are lightweight zones, designed for deploying in edge computing scenarios. They are limited in functionality but have far fewer prerequisites than core zones.

Please refer to the Apache CloudStack documentation for more information on Zone Types
http://docs.cloudstack.apache.org/en/latest/installguide/configuration.html#adding-a-zone", "message.desc.cluster": "Each pod must contain one or more clusters. We will add the first cluster now. A cluster provides a way to group hosts. The hosts in a cluster all have identical hardware, run the same hypervisor, are on the same subnet, and access the same shared storage. Each cluster consists of one or more hosts and one or more primary storage servers.", "message.desc.create.ssh.key.pair": "Please fill in the following data to create or register a ssh key pair.

(1) If public key is set, CloudStack will register the public key. You can use it through your private key.

(2) If public key is not set, CloudStack will create a new SSH key pair. In this case, please copy and save the private key. CloudStack will not keep it.
", "message.desc.created.ssh.key.pair": "Created a SSH key pair.", @@ -2271,6 +2284,8 @@ "message.desc.register.user.data": "Please fill in the following data to register a user data.", "message.desc.registered.user.data": "Registered a User Data.", "message.desc.zone": "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.desc.zone.edge": "A zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. An edge zone consists of one or more hosts (each of which provides local storage as primary storage servers). Only shared and L2 networks can be deployed in such zones and functionalities that require secondary storages are not supported.", +"message.zone.edge.local.storage": "Local storage will be used by default for user VMs and virtual routers", "message.detach.disk": "Are you sure you want to detach this disk?", "message.detach.iso.confirm": "Please confirm that you want to detach the ISO from this virtual instance.", "message.disable.account": "Please confirm that you want to disable this account. By disabling the account, all users for this account will no longer have access to their cloud resources. All running virtual machines will be immediately shut down.", diff --git a/ui/src/components/view/InfoCard.vue b/ui/src/components/view/InfoCard.vue index f7eb3a529db..43d24a8f121 100644 --- a/ui/src/components/view/InfoCard.vue +++ b/ui/src/components/view/InfoCard.vue @@ -631,7 +631,7 @@