diff --git a/api/src/main/java/com/cloud/network/GuestVlan.java b/api/src/main/java/com/cloud/network/GuestVlan.java index 4597192ef606..dff74ddaee68 100644 --- a/api/src/main/java/com/cloud/network/GuestVlan.java +++ b/api/src/main/java/com/cloud/network/GuestVlan.java @@ -16,17 +16,26 @@ // under the License. package com.cloud.network; -import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; -public interface GuestVlan extends InternalIdentity, Identity { +import java.util.Date; + +public interface GuestVlan extends InternalIdentity { @Override public long getId(); - public long getAccountId(); + Date getTakenAt(); + + String getVnet(); + + String getReservationId(); + + Long getAccountId(); + + long getDataCenterId(); - public String getGuestVlanRange(); + long getPhysicalNetworkId(); - public long getPhysicalNetworkId(); + Long getAccountGuestVlanMapId(); } diff --git a/api/src/main/java/com/cloud/network/GuestVlanRange.java b/api/src/main/java/com/cloud/network/GuestVlanRange.java new file mode 100644 index 000000000000..adc5addf8903 --- /dev/null +++ b/api/src/main/java/com/cloud/network/GuestVlanRange.java @@ -0,0 +1,32 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface GuestVlanRange extends InternalIdentity, Identity { + + @Override + public long getId(); + + public long getAccountId(); + + public String getGuestVlanRange(); + + public long getPhysicalNetworkId(); +} diff --git a/api/src/main/java/com/cloud/network/Network.java b/api/src/main/java/com/cloud/network/Network.java index 1940cd0e020d..e7a46f047bc8 100644 --- a/api/src/main/java/com/cloud/network/Network.java +++ b/api/src/main/java/com/cloud/network/Network.java @@ -372,6 +372,8 @@ public void setIp6Address(String ip6Address) { } } + static final String AssociatedNetworkId = "AssociatedNetworkId"; + String getName(); Mode getMode(); diff --git a/api/src/main/java/com/cloud/network/NetworkModel.java b/api/src/main/java/com/cloud/network/NetworkModel.java index e933a1cc7bd3..30226e04980c 100644 --- a/api/src/main/java/com/cloud/network/NetworkModel.java +++ b/api/src/main/java/com/cloud/network/NetworkModel.java @@ -195,6 +195,8 @@ public interface NetworkModel { void checkNetworkPermissions(Account owner, Network network); + void checkNetworkOperatePermissions(Account owner, Network network); + String getDefaultManagementTrafficLabel(long zoneId, HypervisorType hypervisorType); String getDefaultStorageTrafficLabel(long zoneId, HypervisorType hypervisorType); diff --git a/api/src/main/java/com/cloud/network/NetworkService.java b/api/src/main/java/com/cloud/network/NetworkService.java index 297bcbdc8a8a..9f9e39f0f1ba 100644 --- a/api/src/main/java/com/cloud/network/NetworkService.java +++ b/api/src/main/java/com/cloud/network/NetworkService.java @@ -22,6 +22,7 @@ import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin; import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd; import org.apache.cloudstack.api.command.admin.network.ListDedicatedGuestVlanRangesCmd; +import org.apache.cloudstack.api.command.admin.network.ListGuestVlansCmd; import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; @@ -144,9 +145,9 @@ PhysicalNetworkTrafficType addTrafficTypeToPhysicalNetwork(Long physicalNetworkI boolean deletePhysicalNetworkTrafficType(Long id); - GuestVlan dedicateGuestVlanRange(DedicateGuestVlanRangeCmd cmd); + GuestVlanRange dedicateGuestVlanRange(DedicateGuestVlanRangeCmd cmd); - Pair, Integer> listDedicatedGuestVlanRanges(ListDedicatedGuestVlanRangesCmd cmd); + Pair, Integer> listDedicatedGuestVlanRanges(ListDedicatedGuestVlanRangesCmd cmd); boolean releaseDedicatedGuestVlanRange(Long dedicatedGuestVlanRangeId); @@ -180,7 +181,7 @@ IpAddress associateIPToNetwork(long ipId, long networkId) throws InsufficientAdd * @throws ResourceAllocationException */ Network createPrivateNetwork(String networkName, String displayText, long physicalNetworkId, String broadcastUri, String startIp, String endIP, String gateway, - String netmask, long networkOwnerId, Long vpcId, Boolean sourceNat, Long networkOfferingId, Boolean bypassVlanOverlapCheck) throws ResourceAllocationException, ConcurrentOperationException, + String netmask, long networkOwnerId, Long vpcId, Boolean sourceNat, Long networkOfferingId, Boolean bypassVlanOverlapCheck, Long associatedNetworkId) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException; /** @@ -206,4 +207,6 @@ Network createPrivateNetwork(String networkName, String displayText, long physic AcquirePodIpCmdResponse allocatePodIp(Account account, String zoneId, String podId) throws ResourceAllocationException, ConcurrentOperationException; boolean releasePodIp(ReleasePodIpCmdByAdmin ip) throws CloudRuntimeException; + + Pair, Integer> listGuestVlans(ListGuestVlansCmd 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 d7c83f91678e..4e993a71968e 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcService.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcService.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; +import org.apache.cloudstack.api.command.user.vpc.CreatePrivateGatewayCmd; import org.apache.cloudstack.api.command.user.vpc.ListPrivateGatewaysCmd; import org.apache.cloudstack.api.command.user.vpc.ListStaticRoutesCmd; import org.apache.cloudstack.api.command.user.vpc.RestartVPCCmd; @@ -165,8 +166,7 @@ public Pair, Integer> listVpcs(Long id, String vpcName, Stri * @throws ConcurrentOperationException * @throws ResourceAllocationException */ - public PrivateGateway createVpcPrivateGateway(long vpcId, Long physicalNetworkId, String vlan, String ipAddress, String gateway, String netmask, long gatewayOwnerId, - Long networkOfferingId, Boolean isSoruceNat, Long aclId, Boolean bypassVlanOverlapCheck) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException; + public PrivateGateway createVpcPrivateGateway(CreatePrivateGatewayCmd command) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException; /** * Applies VPC private gateway on the backend, so it becomes functional diff --git a/api/src/main/java/com/cloud/offering/NetworkOffering.java b/api/src/main/java/com/cloud/offering/NetworkOffering.java index 594938775c8d..eaff17216342 100644 --- a/api/src/main/java/com/cloud/offering/NetworkOffering.java +++ b/api/src/main/java/com/cloud/offering/NetworkOffering.java @@ -48,6 +48,7 @@ public enum Detail { public final static String SystemManagementNetwork = "System-Management-Network"; public final static String SystemStorageNetwork = "System-Storage-Network"; public final static String SystemPrivateGatewayNetworkOffering = "System-Private-Gateway-Network-Offering"; + public final static String SystemPrivateGatewayNetworkOfferingWithoutVlan = "System-Private-Gateway-Network-Offering-Without-Vlan"; public final static String DefaultSharedNetworkOfferingWithSGService = "DefaultSharedNetworkOfferingWithSGService"; public final static String QuickCloudNoServices = "QuickCloudNoServices"; 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 b19819c36f1a..10c8c23dc689 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -410,6 +410,8 @@ 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_DEDICATED = "isdedicated"; + public static final String TAKEN = "taken"; public static final String VM_AVAILABLE = "vmavailable"; public static final String VM_LIMIT = "vmlimit"; public static final String VM_TOTAL = "vmtotal"; @@ -523,6 +525,7 @@ public class ApiConstants { public static final String ISOLATION_METHOD = "isolationmethod"; public static final String ISOLATION_METHODS = "isolationmethods"; public static final String PHYSICAL_NETWORK_ID = "physicalnetworkid"; + public static final String PHYSICAL_NETWORK_NAME = "physicalnetworkname"; public static final String DEST_PHYSICAL_NETWORK_ID = "destinationphysicalnetworkid"; public static final String ENABLE = "enable"; public static final String ENABLED = "enabled"; 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 03f0a3c8369f..8a1a0814d171 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -23,6 +23,7 @@ import java.util.Set; import com.cloud.server.ResourceIcon; +import org.apache.cloudstack.api.response.GuestVlanResponse; import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse; import com.cloud.resource.RollingMaintenanceManager; @@ -148,6 +149,7 @@ import com.cloud.host.Host; import com.cloud.hypervisor.HypervisorCapabilities; import com.cloud.network.GuestVlan; +import com.cloud.network.GuestVlanRange; import com.cloud.network.IpAddress; import com.cloud.network.Network; import com.cloud.network.Network.Service; @@ -256,7 +258,7 @@ public interface ResponseGenerator { IPAddressResponse createIPAddressResponse(ResponseView view, IpAddress ipAddress); - GuestVlanRangeResponse createDedicatedGuestVlanRangeResponse(GuestVlan result); + GuestVlanRangeResponse createDedicatedGuestVlanRangeResponse(GuestVlanRange result); GlobalLoadBalancerResponse createGlobalLoadBalancerResponse(GlobalLoadBalancerRule globalLoadBalancerRule); @@ -491,4 +493,5 @@ List createTemplateResponses(ResponseView view, VirtualMachine ResourceIconResponse createResourceIconResponse(ResourceIcon resourceIcon); + GuestVlanResponse createGuestVlanResponse(GuestVlan vlan); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateGuestVlanRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateGuestVlanRangeCmd.java index ba7b4b99c25b..e8fc7b383b9f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateGuestVlanRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/DedicateGuestVlanRangeCmd.java @@ -33,7 +33,7 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.network.GuestVlan; +import com.cloud.network.GuestVlanRange; import com.cloud.user.Account; @APICommand(name = "dedicateGuestVlanRange", description = "Dedicates a guest vlan range to an account", responseObject = GuestVlanRangeResponse.class, @@ -106,7 +106,7 @@ public long getEntityOwnerId() { @Override public void execute() throws ResourceUnavailableException, ResourceAllocationException { - GuestVlan result = _networkService.dedicateGuestVlanRange(this); + GuestVlanRange result = _networkService.dedicateGuestVlanRange(this); if (result != null) { GuestVlanRangeResponse response = _responseGenerator.createDedicatedGuestVlanRangeResponse(result); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListDedicatedGuestVlanRangesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListDedicatedGuestVlanRangesCmd.java index 9b1935c1d5ab..617a07b5c8c5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListDedicatedGuestVlanRangesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListDedicatedGuestVlanRangesCmd.java @@ -32,7 +32,7 @@ import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.ZoneResponse; -import com.cloud.network.GuestVlan; +import com.cloud.network.GuestVlanRange; import com.cloud.user.Account; import com.cloud.utils.Pair; @@ -124,10 +124,10 @@ public long getEntityOwnerId() { @Override public void execute() { - Pair, Integer> vlans = _networkService.listDedicatedGuestVlanRanges(this); + Pair, Integer> vlans = _networkService.listDedicatedGuestVlanRanges(this); ListResponse response = new ListResponse(); List guestVlanResponses = new ArrayList(); - for (GuestVlan vlan : vlans.first()) { + for (GuestVlanRange vlan : vlans.first()) { GuestVlanRangeResponse guestVlanResponse = _responseGenerator.createDedicatedGuestVlanRangeResponse(vlan); guestVlanResponse.setObjectName("dedicatedguestvlanrange"); guestVlanResponses.add(guestVlanResponse); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListGuestVlansCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListGuestVlansCmd.java new file mode 100644 index 000000000000..dc9df12467bc --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListGuestVlansCmd.java @@ -0,0 +1,113 @@ +// 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.response.PhysicalNetworkResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.log4j.Logger; + +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.GuestVlanResponse; +import org.apache.cloudstack.api.response.ListResponse; + +import com.cloud.network.GuestVlan; +import com.cloud.utils.Pair; + +@APICommand(name = "listGuestVlans", description = "Lists all guest vlans", responseObject = GuestVlanResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.17.0", + authorized = {RoleType.Admin}) +public class ListGuestVlansCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListGuestVlansCmd.class.getName()); + + private static final String s_name = "listguestvlansresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.LONG, required = false, description = "list guest vlan by id") + private Long id; + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = false, description = "list guest vlan by zone") + private Long zoneId; + + @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = false, description = "list guest vlan by physical network") + private Long physicalNetworkId; + + @Parameter(name = ApiConstants.VNET, type = CommandType.STRING, required = false, description = "list guest vlan by vnet") + private String vnet; + + @Parameter(name = ApiConstants.ALLOCATED_ONLY, type = CommandType.BOOLEAN, required = false, description = "limits search results to allocated guest vlan. false by default.") + private Boolean allocatedOnly; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getZoneId() { + return zoneId; + } + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public String getVnet() { + return vnet; + } + + public Boolean getAllocatedOnly() { + return allocatedOnly; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute() { + Pair, Integer> vlans = _networkService.listGuestVlans(this); + ListResponse response = new ListResponse(); + List guestVlanResponses = new ArrayList(); + for (GuestVlan vlan : vlans.first()) { + GuestVlanResponse guestVlanResponse = _responseGenerator.createGuestVlanResponse(vlan); + guestVlanResponse.setObjectName("guestvlan"); + guestVlanResponses.add(guestVlanResponse); + } + + response.setResponses(guestVlanResponses, vlans.second()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworksCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworksCmdByAdmin.java index a234ff078868..8df1133d5e7c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworksCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListNetworksCmdByAdmin.java @@ -17,6 +17,8 @@ package org.apache.cloudstack.api.command.admin.network; 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.network.ListNetworksCmd; @@ -26,4 +28,16 @@ @APICommand(name = "listNetworks", description = "Lists all available networks.", responseObject = NetworkResponse.class, responseView = ResponseView.Full, entityType = {Network.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class ListNetworksCmdByAdmin extends ListNetworksCmd implements AdminCmd {} +public class ListNetworksCmdByAdmin extends ListNetworksCmd implements AdminCmd { + + @Parameter(name= ApiConstants.VLAN, type=CommandType.STRING, description="the ID or VID of the network", since = "4.17.0") + private String vlan; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getVlan() { + return vlan; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayByAdminCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayByAdminCmd.java new file mode 100644 index 000000000000..b42e3d952855 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayByAdminCmd.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.vpc; + +import org.apache.commons.lang3.BooleanUtils; +import org.apache.log4j.Logger; + +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.CreatePrivateGatewayCmd; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.api.response.PrivateGatewayResponse; + +import com.cloud.network.vpc.VpcGateway; + +@APICommand(name = "createPrivateGateway", description = "Creates a private gateway", + responseObject = PrivateGatewayResponse.class, + responseView = ResponseView.Full, + entityType = {VpcGateway.class}, + since = "4.17.0", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class CreatePrivateGatewayByAdminCmd extends CreatePrivateGatewayCmd implements AdminCmd { + public static final Logger s_logger = Logger.getLogger(CreatePrivateGatewayByAdminCmd.class.getName()); + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, + type = CommandType.UUID, + entityType = PhysicalNetworkResponse.class, + description = "the Physical Network ID the network belongs to") + private Long physicalNetworkId; + + @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, description = "the network implementation uri for the private gateway") + private String broadcastUri; + + @Parameter(name = ApiConstants.BYPASS_VLAN_OVERLAP_CHECK, type = CommandType.BOOLEAN, description = "when true bypasses VLAN id/range overlap check during private gateway creation") + private Boolean bypassVlanOverlapCheck; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public String getBroadcastUri() { + return broadcastUri; + } + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public Boolean getBypassVlanOverlapCheck() { + return BooleanUtils.toBoolean(bypassVlanOverlapCheck); + } +} \ No newline at end of file diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/DeletePrivateGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/DeletePrivateGatewayCmd.java index 8d67a4ed8c50..1126779f45be 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/DeletePrivateGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/DeletePrivateGatewayCmd.java @@ -18,6 +18,7 @@ import org.apache.log4j.Logger; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; @@ -37,7 +38,8 @@ import com.cloud.user.Account; @APICommand(name = "deletePrivateGateway", description = "Deletes a Private gateway", responseObject = SuccessResponse.class, entityType = {VpcGateway.class}, - requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class DeletePrivateGatewayCmd extends BaseAsyncCmd { public static final Logger s_logger = Logger.getLogger(DeletePrivateGatewayCmd.class.getName()); private static final String s_name = "deleteprivategatewayresponse"; 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 5dff7c9bf2bb..10aadee40e60 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 @@ -112,7 +112,7 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account that will own the network") private String accountName; - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the SSH key") + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the network") private Long projectId; @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain ID of the account owning a network") @@ -150,6 +150,13 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { @Parameter(name = ApiConstants.ACL_ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, description = "Network ACL ID associated for the network") private Long aclId; + @Parameter(name = ApiConstants.ASSOCIATED_NETWORK_ID, + type = CommandType.UUID, + entityType = NetworkResponse.class, + since = "4.17.0", + description = "The network this network is associated to. only available if create a Shared network") + private Long associatedNetworkId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -225,6 +232,10 @@ public String getIsolatedPvlanType() { return isolatedPvlanType; } + public Long getAssociatedNetworkId() { + return associatedNetworkId; + } + @Override public boolean isDisplay() { if(displayNetwork == null) @@ -249,11 +260,31 @@ public Long getPhysicalNetworkId() { throw new InvalidParameterValueException("Unable to find network offering by ID " + networkOfferingId); } + Network associatedNetwork = null; + if (associatedNetworkId != null) { + associatedNetwork = _entityMgr.findById(Network.class, associatedNetworkId); + if (associatedNetwork == null) { + throw new InvalidParameterValueException("Unable to find network by ID " + associatedNetworkId); + } + if (offering.getGuestType() != GuestType.Shared) { + throw new InvalidParameterValueException("Associated network ID can be specified for networks of guest IP type " + GuestType.Shared + " only."); + } + if (zoneId != null && associatedNetwork.getDataCenterId() != zoneId) { + throw new InvalidParameterValueException("The network can only be created in the same zone as the associated network"); + } else if (zoneId == null) { + zoneId = associatedNetwork.getDataCenterId(); + } + if (physicalNetworkId != null && !physicalNetworkId.equals(associatedNetwork.getPhysicalNetworkId())) { + throw new InvalidParameterValueException("The network can only be created on the same physical network as the associated network"); + } else if (physicalNetworkId == null) { + physicalNetworkId = associatedNetwork.getPhysicalNetworkId(); + } + } if (physicalNetworkId != null) { if (offering.getGuestType() == GuestType.Shared) { return physicalNetworkId; } else { - throw new InvalidParameterValueException("Physical network OD can be specified for networks of guest IP type " + GuestType.Shared + " only."); + throw new InvalidParameterValueException("Physical network ID can be specified for networks of guest IP type " + GuestType.Shared + " only."); } } else { if (zoneId == null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmd.java index 72a953857afd..c8e49f414f33 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/DeleteNetworkCmd.java @@ -16,12 +16,13 @@ // under the License. package org.apache.cloudstack.api.command.user.network; -import org.apache.cloudstack.api.ApiCommandJobType; + import org.apache.log4j.Logger; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseAsyncCmd; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java index 315a58a2f5a1..6a8a2b5df5fc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java @@ -96,6 +96,13 @@ public class ListNetworksCmd extends BaseListTaggedResourcesCmd implements UserC @Parameter(name = ApiConstants.NETWORK_OFFERING_ID, type = CommandType.UUID, entityType = NetworkOfferingResponse.class, description = "list networks by network offering ID") private Long networkOfferingId; + @Parameter(name = ApiConstants.ASSOCIATED_NETWORK_ID, + type = CommandType.UUID, + entityType = NetworkResponse.class, + since = "4.17.0", + description = "List networks by associated networks. Only available if create a Shared network.") + private Long associatedNetworkId; + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, description = "flag to display the resource icon for networks") private Boolean showIcon; @@ -166,6 +173,10 @@ public Long getNetworkOfferingId() { return networkOfferingId; } + public Long getAssociatedNetworkId() { + return associatedNetworkId; + } + @Override public Boolean getDisplay() { if (display != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/RestartNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/RestartNetworkCmd.java index dbcdb8f88846..59879562b02c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/RestartNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/RestartNetworkCmd.java @@ -21,6 +21,7 @@ import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseAsyncCmd; @@ -124,6 +125,16 @@ public String getEventType() { return EventTypes.EVENT_NETWORK_RESTART; } + @Override + public Long getInstanceId() { + return getNetworkId(); + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.Network; + } + @Override public long getEntityOwnerId() { Network network = _networkService.getNetwork(id); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java index 2ffa52bcfd2e..7a20ceafd771 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java @@ -20,6 +20,7 @@ import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseAsyncCmd; @@ -144,6 +145,16 @@ public String getCommandName() { return s_name; } + @Override + public Long getInstanceId() { + return getId(); + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.Network; + } + @Override public long getEntityOwnerId() { Network network = _networkService.getNetwork(id); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreatePrivateGatewayCmd.java similarity index 81% rename from api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayCmd.java rename to api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreatePrivateGatewayCmd.java index fab1b5983519..5e5e43cf609d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreatePrivateGatewayCmd.java @@ -14,10 +14,11 @@ // 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; +package org.apache.cloudstack.api.command.user.vpc; import org.apache.log4j.Logger; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; @@ -25,10 +26,12 @@ import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.BaseAsyncCreateCmd; import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.NetworkACLResponse; import org.apache.cloudstack.api.response.NetworkOfferingResponse; -import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.api.response.NetworkResponse; import org.apache.cloudstack.api.response.PrivateGatewayResponse; import org.apache.cloudstack.api.response.VpcResponse; @@ -41,11 +44,14 @@ import com.cloud.network.vpc.PrivateGateway; import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.VpcGateway; -import com.cloud.user.Account; -@APICommand(name = "createPrivateGateway", description = "Creates a private gateway", responseObject = PrivateGatewayResponse.class, entityType = {VpcGateway.class}, - requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd { +@APICommand(name = "createPrivateGateway", description = "Creates a private gateway", + responseObject = PrivateGatewayResponse.class, + responseView = ResponseView.Restricted, + entityType = {VpcGateway.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd implements UserCmd { public static final Logger s_logger = Logger.getLogger(CreatePrivateGatewayCmd.class.getName()); private static final String s_name = "createprivategatewayresponse"; @@ -54,12 +60,6 @@ public class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, - type = CommandType.UUID, - entityType = PhysicalNetworkResponse.class, - description = "the Physical Network ID the network belongs to") - private Long physicalNetworkId; - @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, required = true, description = "the gateway of the Private gateway") private String gateway; @@ -69,9 +69,6 @@ public class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, required = true, description = "the IP address of the Private gateaway") private String ipAddress; - @Parameter(name = ApiConstants.VLAN, type = CommandType.STRING, required = true, description = "the network implementation uri for the private gateway") - private String broadcastUri; - @Parameter(name = ApiConstants.NETWORK_OFFERING_ID, type = CommandType.UUID, required = false, @@ -92,8 +89,12 @@ public class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.ACL_ID, type = CommandType.UUID, entityType = NetworkACLResponse.class, required = false, description = "the ID of the network ACL") private Long aclId; - @Parameter(name=ApiConstants.BYPASS_VLAN_OVERLAP_CHECK, type=CommandType.BOOLEAN, description="when true bypasses VLAN id/range overlap check during private gateway creation") - private Boolean bypassVlanOverlapCheck; + @Parameter(name = ApiConstants.ASSOCIATED_NETWORK_ID, + type = CommandType.UUID, + entityType = NetworkResponse.class, + since = "4.17.0", + description = "The isolated network this private gateway is associated to.") + private Long associatedNetworkId; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -103,23 +104,15 @@ public String getGateway() { return gateway; } - public String getBroadcastUri() { - return broadcastUri; - } - public String getNetmask() { return netmask; } - public String getStartIp() { + public String getIpAddress() { return ipAddress; } - public Long getPhysicalNetworkId() { - return physicalNetworkId; - } - - private Long getNetworkOfferingId() { + public Long getNetworkOfferingId() { return networkOfferingId; } @@ -138,11 +131,8 @@ public Long getAclId() { return aclId; } - public Boolean getBypassVlanOverlapCheck() { - if (bypassVlanOverlapCheck != null) { - return bypassVlanOverlapCheck; - } - return false; + public Long getAssociatedNetworkId() { + return associatedNetworkId; } ///////////////////////////////////////////////////// @@ -157,9 +147,7 @@ public String getCommandName() { public void create() throws ResourceAllocationException { PrivateGateway result = null; try { - result = - _vpcService.createVpcPrivateGateway(getVpcId(), getPhysicalNetworkId(), getBroadcastUri(), getStartIp(), getGateway(), getNetmask(), getEntityOwnerId(), - getNetworkOfferingId(), getIsSourceNat(), getAclId(), getBypassVlanOverlapCheck()); + result = _vpcService.createVpcPrivateGateway(this); } catch (InsufficientCapacityException ex) { s_logger.info(ex); s_logger.trace(ex); @@ -191,7 +179,11 @@ public void execute() throws InsufficientCapacityException, ConcurrentOperationE @Override public long getEntityOwnerId() { - return Account.ACCOUNT_ID_SYSTEM; + Vpc vpc = _entityMgr.findById(Vpc.class, vpcId); + if (vpc == null) { + throw new InvalidParameterValueException("Invalid id is specified for the vpc"); + } + return vpc.getAccountId(); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanRangeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanRangeResponse.java index 16ce223c08c1..fab2a2cb4ce3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanRangeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanRangeResponse.java @@ -22,10 +22,10 @@ import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; -import com.cloud.network.GuestVlan; +import com.cloud.network.GuestVlanRange; import com.cloud.serializer.Param; -@EntityReference(value = GuestVlan.class) +@EntityReference(value = GuestVlanRange.class) @SuppressWarnings("unused") public class GuestVlanRangeResponse extends BaseResponse implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanResponse.java new file mode 100644 index 000000000000..6bcc1d35cb15 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuestVlanResponse.java @@ -0,0 +1,156 @@ +// 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.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; + +import java.util.Date; +import java.util.List; + +@SuppressWarnings("unused") +public class GuestVlanResponse extends BaseResponse implements ControlledEntityResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "the guest VLAN id") + private long id; + + @SerializedName(ApiConstants.VLAN) + @Param(description = "the guest VLAN") + private String guestVlan; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account of the guest VLAN range") + private String accountName; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the domain ID of the guest VLAN range") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the domain name of the guest VLAN range") + private String domainName; + + @SerializedName(ApiConstants.PROJECT_ID) + @Param(description = "the project id of the guest VLAN range") + private String projectId; + + @SerializedName(ApiConstants.PROJECT) + @Param(description = "the project name of the guest VLAN range") + private String projectName; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "the zone ID of the guest VLAN range") + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "the zone name of the guest VLAN range") + private String zoneName; + + @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) + @Param(description = "the physical network ID of the guest VLAN range") + private String physicalNetworkId; + + @SerializedName(ApiConstants.PHYSICAL_NETWORK_NAME) + @Param(description = "the physical network name of the guest VLAN range") + private String physicalNetworkName; + + @SerializedName(ApiConstants.IS_DEDICATED) + @Param(description = "true if the guest VLAN is dedicated to the account") + private Boolean isDedicated; + + @SerializedName(ApiConstants.ALLOCATION_STATE) + @Param(description = "the allocation state of the guest VLAN") + private String allocationState; + + @SerializedName(ApiConstants.TAKEN) + @Param(description = "date the guest VLAN was taken") + private Date taken; + + @SerializedName(ApiConstants.NETWORK) + @Param(description = "the list of networks who use this guest VLAN", responseObject = NetworkResponse.class) + private List networks; + + public void setId(long id) { + this.id = id; + } + + @Override + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + @Override + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + @Override + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + @Override + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + @Override + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public void setPhysicalNetworkId(String physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public void setPhysicalNetworkName(String physicalNetworkName) { + this.physicalNetworkName = physicalNetworkName; + } + + public void setGuestVlan(String guestVlan) { + this.guestVlan = guestVlan; + } + + public void setDedicated(Boolean dedicated) { + isDedicated = dedicated; + } + + public void setAllocationState(String allocationState) { + this.allocationState = allocationState; + } + + public void setTaken(Date taken) { + this.taken = taken; + } + + public void setNetworks(List networks) { + this.networks = networks; + } +} 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 4b41610ab02a..17397ced72de 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 @@ -195,6 +195,14 @@ public class NetworkResponse extends BaseResponseWithAnnotations implements Cont @Param(description = "Name of the VPC to which this network belongs", since = "4.15") private String vpcName; + @SerializedName(ApiConstants.ASSOCIATED_NETWORK_ID) + @Param(description = "the ID of the Network associated with this network") + private String associatedNetworkId; + + @SerializedName(ApiConstants.ASSOCIATED_NETWORK_NAME) + @Param(description = "the name of the Network associated with this network") + private String associatedNetworkName; + @SerializedName(ApiConstants.CAN_USE_FOR_DEPLOY) @Param(description = "list networks available for vm deployment") private Boolean canUseForDeploy; @@ -508,6 +516,14 @@ public void setVpcName(String vpcName) { this.vpcName = vpcName; } + public void setAssociatedNetworkId(String associatedNetworkId) { + this.associatedNetworkId = associatedNetworkId; + } + + public void setAssociatedNetworkName(String associatedNetworkName) { + this.associatedNetworkName = associatedNetworkName; + } + @Override public void setResourceIconResponse(ResourceIconResponse icon) { this.icon = icon; 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 1673575780cb..9a08aebc8ab0 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 @@ -177,6 +177,8 @@ void prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationC */ void rollbackNicForMigration(VirtualMachineProfile src, VirtualMachineProfile dst); + boolean isSharedNetworkWithoutSpecifyVlan(NetworkOffering offering); + boolean shutdownNetwork(long networkId, ReservationContext context, boolean cleanupElements); boolean destroyNetwork(long networkId, ReservationContext context, boolean forced); 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 1c303ad7f259..89ea63f38c76 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 @@ -2530,7 +2530,9 @@ private Network createGuestNetwork(final long networkOfferingId, final String na final boolean vlanSpecified = vlanId != null; if (vlanSpecified != ntwkOff.isSpecifyVlan()) { if (vlanSpecified) { - throw new InvalidParameterValueException("Can't specify vlan; corresponding offering says specifyVlan=false"); + if (!isSharedNetworkWithoutSpecifyVlan(ntwkOff) && !isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { + throw new InvalidParameterValueException("Can't specify vlan; corresponding offering says specifyVlan=false"); + } } else { throw new InvalidParameterValueException("Vlan has to be specified; corresponding offering says specifyVlan=true"); } @@ -2540,8 +2542,12 @@ private Network createGuestNetwork(final long networkOfferingId, final String na URI uri = encodeVlanIdIntoBroadcastUri(vlanId, pNtwk); // Aux: generate secondary URI for secondary VLAN ID (if provided) for performing checks URI secondaryUri = StringUtils.isNotBlank(isolatedPvlan) ? BroadcastDomainType.fromString(isolatedPvlan) : null; + if (isSharedNetworkWithoutSpecifyVlan(ntwkOff) || isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { + bypassVlanOverlapCheck = true; + } //don't allow to specify vlan tag used by physical network for dynamic vlan allocation - if (!(bypassVlanOverlapCheck && ntwkOff.getGuestType() == GuestType.Shared) && _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(uri)).size() > 0) { + if (!(bypassVlanOverlapCheck && (ntwkOff.getGuestType() == GuestType.Shared || isPrivateNetwork)) + && _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(uri)).size() > 0) { throw new InvalidParameterValueException("The VLAN tag to use for new guest network, " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone " + zone.getName()); } @@ -2764,6 +2770,18 @@ public Network doInTransaction(final TransactionStatus status) { return network; } + @Override + public boolean isSharedNetworkWithoutSpecifyVlan(NetworkOffering offering) { + if (offering == null || offering.getTrafficType() != TrafficType.Guest || offering.getGuestType() != GuestType.Shared) { + return false; + } + return !offering.isSpecifyVlan(); + } + + private boolean isPrivateGatewayWithoutSpecifyVlan(NetworkOffering ntwkOff) { + return ntwkOff.getId() == _networkOfferingDao.findByUniqueName(NetworkOffering.SystemPrivateGatewayNetworkOfferingWithoutVlan).getId(); + } + /** * Encodes VLAN/VXLAN ID into a Broadcast URI according to the isolation method from the Physical Network. * @return Broadcast URI, e.g. 'vlan://vlan_ID' or 'vxlan://vlxan_ID' @@ -3093,7 +3111,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { throw new CloudRuntimeException("Failed to trash network."); } - if (!deleteVlansInNetwork(networkFinal.getId(), context.getCaller().getId(), callerAccount)) { + if (!deleteVlansInNetwork(networkFinal, context.getCaller().getId(), callerAccount)) { s_logger.warn("Failed to delete network " + networkFinal + "; was unable to cleanup corresponding ip ranges"); throw new CloudRuntimeException("Failed to delete network " + networkFinal + "; was unable to cleanup corresponding ip ranges"); } else { @@ -3147,8 +3165,8 @@ public boolean resourceCountNeedsUpdate(final NetworkOffering ntwkOff, final ACL return updateResourceCount; } - protected boolean deleteVlansInNetwork(final long networkId, final long userId, final Account callerAccount) { - + protected boolean deleteVlansInNetwork(final NetworkVO network, final long userId, final Account callerAccount) { + final long networkId = network.getId(); //cleanup Public vlans final List publicVlans = _vlanDao.listVlansByNetworkId(networkId); boolean result = true; @@ -3168,6 +3186,13 @@ protected boolean deleteVlansInNetwork(final long networkId, final long userId, _privateIpDao.deleteByNetworkId(networkId); s_logger.debug("Deleted ip range for private network id=" + networkId); } + + // release vlans of user-shared networks without specifyvlan + if (isSharedNetworkWithoutSpecifyVlan(_networkOfferingDao.findById(network.getNetworkOfferingId()))) { + s_logger.debug("Releasing vnet for the network id=" + network.getId()); + _dcDao.releaseVnet(BroadcastDomainType.getValue(network.getBroadcastUri()), network.getDataCenterId(), + network.getPhysicalNetworkId(), network.getAccountId(), network.getReservationId()); + } return result; } @@ -3203,6 +3228,11 @@ public void reallyRun() { continue; } + if (!networkDetailsDao.findDetails(Network.AssociatedNetworkId, String.valueOf(networkId), null).isEmpty()) { + s_logger.debug(String.format("Network %s is associated to a shared network, skipping", networkId)); + continue; + } + final Long time = _lastNetworkIdsToFree.remove(networkId); if (time == null) { if (s_logger.isDebugEnabled()) { diff --git a/engine/schema/src/main/java/com/cloud/dc/DataCenterVnetVO.java b/engine/schema/src/main/java/com/cloud/dc/DataCenterVnetVO.java index d3959df8af8e..b2b12bba6863 100644 --- a/engine/schema/src/main/java/com/cloud/dc/DataCenterVnetVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/DataCenterVnetVO.java @@ -27,11 +27,11 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; -import org.apache.cloudstack.api.InternalIdentity; +import com.cloud.network.GuestVlan; @Entity @Table(name = "op_dc_vnet_alloc") -public class DataCenterVnetVO implements InternalIdentity { +public class DataCenterVnetVO implements GuestVlan { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -84,6 +84,7 @@ public String getVnet() { return vnet; } + @Override public String getReservationId() { return reservationId; } @@ -92,6 +93,7 @@ public void setReservationId(String reservationId) { this.reservationId = reservationId; } + @Override public Long getAccountId() { return accountId; } @@ -100,10 +102,12 @@ public void setAccountId(Long accountId) { this.accountId = accountId; } + @Override public long getDataCenterId() { return dataCenterId; } + @Override public long getPhysicalNetworkId() { return physicalNetworkId; } @@ -112,6 +116,7 @@ public void setAccountGuestVlanMapId(Long accountGuestVlanMapId) { this.accountGuestVlanMapId = accountGuestVlanMapId; } + @Override public Long getAccountGuestVlanMapId() { return accountGuestVlanMapId; } diff --git a/engine/schema/src/main/java/com/cloud/dc/VlanVO.java b/engine/schema/src/main/java/com/cloud/dc/VlanVO.java index 20b07e983b86..7423ded598f3 100644 --- a/engine/schema/src/main/java/com/cloud/dc/VlanVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/VlanVO.java @@ -205,8 +205,8 @@ public String toString() { .append("|") .append(ipRange) .append("|") - .append("|") .append(ip6Range) + .append("|") .append(networkId) .append("]") .toString(); diff --git a/engine/schema/src/main/java/com/cloud/network/dao/AccountGuestVlanMapVO.java b/engine/schema/src/main/java/com/cloud/network/dao/AccountGuestVlanMapVO.java index 4234c73441fe..2826abf9cacc 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/AccountGuestVlanMapVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/AccountGuestVlanMapVO.java @@ -25,11 +25,11 @@ import javax.persistence.Id; import javax.persistence.Table; -import com.cloud.network.GuestVlan; +import com.cloud.network.GuestVlanRange; @Entity @Table(name = "account_vnet_map") -public class AccountGuestVlanMapVO implements GuestVlan { +public class AccountGuestVlanMapVO implements GuestVlanRange { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) 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 338ef57513b0..ff187687fdf4 100644 --- a/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java @@ -402,12 +402,12 @@ public NetworkOfferingVO(String name, TrafficType trafficType, boolean specifyIp this.state = State.Enabled; } - public NetworkOfferingVO(String name, Network.GuestType guestType) { + public NetworkOfferingVO(String name, Network.GuestType guestType, boolean specifyVlan) { this(name, "System Offering for " + name, TrafficType.Guest, true, - true, + specifyVlan, 0, 0, true, diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/DomainRouterDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/DomainRouterDaoImpl.java index dc71f898970b..85e751788691 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/DomainRouterDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/DomainRouterDaoImpl.java @@ -23,6 +23,7 @@ import javax.inject.Inject; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import com.cloud.host.HostVO; @@ -376,7 +377,7 @@ public DomainRouterVO persist(final DomainRouterVO router, final List g public void addRouterToGuestNetwork(final VirtualRouter router, final Network guestNetwork) { if (_routerNetworkDao.findByRouterAndNetwork(router.getId(), guestNetwork.getId()) == null) { final NetworkOffering off = _offDao.findById(guestNetwork.getNetworkOfferingId()); - if (!off.getName().equalsIgnoreCase(NetworkOffering.SystemPrivateGatewayNetworkOffering)) { + if (!StringUtils.equalsAnyIgnoreCase(NetworkOffering.SystemPrivateGatewayNetworkOffering, NetworkOffering.SystemPrivateGatewayNetworkOfferingWithoutVlan)) { final TransactionLegacy txn = TransactionLegacy.currentTxn(); txn.start(); //1) add router to network diff --git a/server/src/main/java/com/cloud/acl/DomainChecker.java b/server/src/main/java/com/cloud/acl/DomainChecker.java index aba0d456bfa2..d4826a261770 100644 --- a/server/src/main/java/com/cloud/acl/DomainChecker.java +++ b/server/src/main/java/com/cloud/acl/DomainChecker.java @@ -173,6 +173,8 @@ public boolean checkAccess(Account caller, ControlledEntity entity, AccessType a return true; } else if (entity instanceof Network && accessType != null && accessType == AccessType.UseEntry) { _networkMgr.checkNetworkPermissions(caller, (Network)entity); + } else if (entity instanceof Network && accessType != null && accessType == AccessType.OperateEntry) { + _networkMgr.checkNetworkOperatePermissions(caller, (Network)entity); } else if (entity instanceof AffinityGroup) { return false; } else { diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 44757798b755..8376711278dc 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -79,6 +79,7 @@ import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.GuestOsMappingResponse; import org.apache.cloudstack.api.response.GuestVlanRangeResponse; +import org.apache.cloudstack.api.response.GuestVlanResponse; import org.apache.cloudstack.api.response.HostForMigrationResponse; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse; @@ -237,6 +238,7 @@ import com.cloud.host.HostVO; import com.cloud.hypervisor.HypervisorCapabilities; import com.cloud.network.GuestVlan; +import com.cloud.network.GuestVlanRange; import com.cloud.network.IpAddress; import com.cloud.network.Network; import com.cloud.network.Network.Capability; @@ -269,6 +271,7 @@ import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkDetailVO; import com.cloud.network.dao.NetworkDetailsDao; import com.cloud.network.dao.NetworkVO; @@ -413,6 +416,8 @@ public class ApiResponseHelper implements ResponseGenerator { private AnnotationDao annotationDao; @Inject private UserStatisticsDao userStatsDao; + @Inject + private NetworkDao networkDao; @Override public UserResponse createUserResponse(User user) { @@ -2424,6 +2429,17 @@ public NetworkResponse createNetworkResponse(ResponseView view, Network network) response.setVpcName(vpc.getName()); } } + + final NetworkDetailVO detail = networkDetailsDao.findDetail(network.getId(), Network.AssociatedNetworkId); + if (detail != null) { + Long associatedNetworkId = Long.valueOf(detail.getValue()); + NetworkVO associatedNetwork = ApiDBUtils.findNetworkById(associatedNetworkId); + if (associatedNetwork != null) { + response.setAssociatedNetworkId(associatedNetwork.getUuid()); + response.setAssociatedNetworkName(associatedNetwork.getName()); + } + } + response.setCanUseForDeploy(ApiDBUtils.canUseForDeploy(network)); // set tag information @@ -2748,7 +2764,7 @@ public PhysicalNetworkResponse createPhysicalNetworkResponse(PhysicalNetwork res } @Override - public GuestVlanRangeResponse createDedicatedGuestVlanRangeResponse(GuestVlan vlan) { + public GuestVlanRangeResponse createDedicatedGuestVlanRangeResponse(GuestVlanRange vlan) { GuestVlanRangeResponse guestVlanRangeResponse = new GuestVlanRangeResponse(); guestVlanRangeResponse.setId(vlan.getUuid()); @@ -4542,4 +4558,54 @@ public RollingMaintenanceResponse createRollingMaintenanceResponse(Boolean succe public ResourceIconResponse createResourceIconResponse(ResourceIcon resourceIcon) { return ApiDBUtils.newResourceIconResponse(resourceIcon); } + + @Override + public GuestVlanResponse createGuestVlanResponse(GuestVlan guestVlan) { + GuestVlanResponse guestVlanResponse = new GuestVlanResponse(); + + Account owner = null; + if (guestVlan.getAccountId() != null) { + owner = ApiDBUtils.findAccountById(guestVlan.getAccountId()); + } else if (guestVlan.getAccountGuestVlanMapId() != null) { + Long accountId = ApiDBUtils.getAccountIdForGuestVlan(guestVlan.getAccountGuestVlanMapId()); + owner = ApiDBUtils.findAccountById(accountId); + } + if (owner != null) { + populateAccount(guestVlanResponse, owner.getId()); + populateDomain(guestVlanResponse, owner.getDomainId()); + } + guestVlanResponse.setId(guestVlan.getId()); + guestVlanResponse.setGuestVlan(guestVlan.getVnet()); + DataCenterVO zone = ApiDBUtils.findZoneById(guestVlan.getDataCenterId()); + if (zone != null) { + guestVlanResponse.setZoneId(zone.getUuid()); + guestVlanResponse.setZoneName(zone.getName()); + } + PhysicalNetworkVO pnw = ApiDBUtils.findPhysicalNetworkById(guestVlan.getPhysicalNetworkId()); + if (pnw != null) { + guestVlanResponse.setPhysicalNetworkId(pnw.getUuid()); + guestVlanResponse.setPhysicalNetworkName(pnw.getName()); + } + if (guestVlan.getAccountGuestVlanMapId() != null) { + guestVlanResponse.setDedicated(true); + } else { + guestVlanResponse.setDedicated(false); + } + if (guestVlan.getTakenAt() != null) { + guestVlanResponse.setAllocationState("Allocated"); + guestVlanResponse.setTaken(guestVlan.getTakenAt()); + } else { + guestVlanResponse.setAllocationState("Free"); + } + + List networks = networkDao.listByZoneAndUriAndGuestType(guestVlan.getDataCenterId(), guestVlan.getVnet(), null); + List networkResponses = new ArrayList(); + for (Network network : networks) { + NetworkResponse ntwkRsp = createNetworkResponse(ResponseView.Full, network); + networkResponses.add(ntwkRsp); + } + guestVlanResponse.setNetworks(networkResponses); + + return guestVlanResponse; + } } diff --git a/server/src/main/java/com/cloud/api/dispatch/ParamProcessWorker.java b/server/src/main/java/com/cloud/api/dispatch/ParamProcessWorker.java index a8506342b728..2218493bcb1f 100644 --- a/server/src/main/java/com/cloud/api/dispatch/ParamProcessWorker.java +++ b/server/src/main/java/com/cloud/api/dispatch/ParamProcessWorker.java @@ -38,7 +38,9 @@ import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.BaseAsyncCreateCmd; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.BaseCmd.CommandType; @@ -277,7 +279,14 @@ private void doAccessChecks(BaseCmd cmd, Map entitiesToAcces if (entityOwners != null) { owners = entityOwners.stream().map(id -> _accountMgr.getAccount(id)).toArray(Account[]::new); } else { - owners = new Account[]{_accountMgr.getAccount(cmd.getEntityOwnerId())}; + if (cmd.getEntityOwnerId() == Account.ACCOUNT_ID_SYSTEM && cmd instanceof BaseAsyncCmd && ((BaseAsyncCmd)cmd).getInstanceType() == ApiCommandJobType.Network) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Skipping access check on the network owner if the owner is ROOT/system."); + } + owners = new Account[]{}; + } else { + owners = new Account[]{_accountMgr.getAccount(cmd.getEntityOwnerId())}; + } } if (cmd instanceof BaseAsyncCreateCmd) { diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index a17d8b44caf4..196a7f60406b 100755 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -112,6 +112,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -4239,6 +4240,7 @@ public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, } } + boolean isSharedNetworkWithoutSpecifyVlan = _networkMgr.isSharedNetworkWithoutSpecifyVlan(_networkOfferingDao.findById(network.getNetworkOfferingId())); if (ipv4) { final String newCidr = NetUtils.getCidrFromGatewayAndNetmask(vlanGateway, vlanNetmask); @@ -4263,69 +4265,8 @@ public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, checkConflictsWithPortableIpRange(zoneId, vlanId, vlanGateway, vlanNetmask, startIP, endIP); - // Throw an exception if this subnet overlaps with subnet on other VLAN, - // if this is ip range extension, gateway, network mask should be same and ip range should not overlap - - final List vlans = _vlanDao.listByZone(zone.getId()); - for (final VlanVO vlan : vlans) { - final String otherVlanGateway = vlan.getVlanGateway(); - final String otherVlanNetmask = vlan.getVlanNetmask(); - // Continue if it's not IPv4 - if ( otherVlanGateway == null || otherVlanNetmask == null ) { - continue; - } - if ( vlan.getNetworkId() == null ) { - continue; - } - final String otherCidr = NetUtils.getCidrFromGatewayAndNetmask(otherVlanGateway, otherVlanNetmask); - if( !NetUtils.isNetworksOverlap(newCidr, otherCidr)) { - continue; - } - // from here, subnet overlaps - if (vlanId.toLowerCase().contains(Vlan.UNTAGGED) || UriUtils.checkVlanUriOverlap( - BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlanId)), - BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlan.getVlanTag())))) { - // For untagged VLAN Id and overlapping URIs we need to expand and verify IP ranges - final String[] otherVlanIpRange = vlan.getIpRange().split("\\-"); - final String otherVlanStartIP = otherVlanIpRange[0]; - String otherVlanEndIP = null; - if (otherVlanIpRange.length > 1) { - otherVlanEndIP = otherVlanIpRange[1]; - } - - // extend IP range - if (!vlanGateway.equals(otherVlanGateway) || !vlanNetmask.equals(vlan.getVlanNetmask())) { - throw new InvalidParameterValueException("The IP range has already been added with gateway " - + otherVlanGateway + " ,and netmask " + otherVlanNetmask - + ", Please specify the gateway/netmask if you want to extend ip range" ); - } - if (!NetUtils.is31PrefixCidr(newCidr)) { - if (NetUtils.ipRangesOverlap(startIP, endIP, otherVlanStartIP, otherVlanEndIP)) { - throw new InvalidParameterValueException("The IP range already has IPs that overlap with the new range." + - " Please specify a different start IP/end IP."); - } - } - } else { - // For tagged or non-overlapping URIs we need to ensure there is no Public traffic type - boolean overlapped = false; - if (network.getTrafficType() == TrafficType.Public) { - overlapped = true; - } else { - final Long nwId = vlan.getNetworkId(); - if (nwId != null) { - final Network nw = _networkModel.getNetwork(nwId); - if (nw != null && nw.getTrafficType() == TrafficType.Public) { - overlapped = true; - } - } - - } - if (overlapped) { - throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag() - + " in zone " + zone.getName() - + " has overlapped with the subnet. Please specify a different gateway/netmask."); - } - } + if (!isSharedNetworkWithoutSpecifyVlan) { + checkZoneVlanIpOverlap(zone, network, newCidr, vlanId, vlanGateway, vlanNetmask, startIP, endIP); } } @@ -4356,6 +4297,9 @@ public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, } // Check if the vlan is being used + if (isSharedNetworkWithoutSpecifyVlan) { + bypassVlanOverlapCheck = true; + } if (!bypassVlanOverlapCheck && _zoneDao.findVnet(zoneId, physicalNetworkId, BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlanId))).size() > 0) { throw new InvalidParameterValueException("The VLAN tag " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone " + zone.getName()); @@ -4377,6 +4321,68 @@ public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, return vlan; } + private void checkZoneVlanIpOverlap(DataCenterVO zone, Network network, String newCidr, String vlanId, String vlanGateway, String vlanNetmask, String startIP, String endIP) { + // Throw an exception if this subnet overlaps with subnet on other VLAN, + // if this is ip range extension, gateway, network mask should be same and ip range should not overlap + + final List vlans = _vlanDao.listByZone(zone.getId()); + for (final VlanVO vlan : vlans) { + final String otherVlanGateway = vlan.getVlanGateway(); + final String otherVlanNetmask = vlan.getVlanNetmask(); + // Continue if it's not IPv4 + if (ObjectUtils.anyNull(otherVlanGateway, otherVlanNetmask, vlan.getNetworkId())) { + continue; + } + final String otherCidr = NetUtils.getCidrFromGatewayAndNetmask(otherVlanGateway, otherVlanNetmask); + if( !NetUtils.isNetworksOverlap(newCidr, otherCidr)) { + continue; + } + // from here, subnet overlaps + if (vlanId.toLowerCase().contains(Vlan.UNTAGGED) || UriUtils.checkVlanUriOverlap( + BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlanId)), + BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlan.getVlanTag())))) { + // For untagged VLAN Id and overlapping URIs we need to expand and verify IP ranges + final String[] otherVlanIpRange = vlan.getIpRange().split("\\-"); + final String otherVlanStartIP = otherVlanIpRange[0]; + String otherVlanEndIP = null; + if (otherVlanIpRange.length > 1) { + otherVlanEndIP = otherVlanIpRange[1]; + } + + // extend IP range + if (!vlanGateway.equals(otherVlanGateway) || !vlanNetmask.equals(vlan.getVlanNetmask())) { + throw new InvalidParameterValueException("The IP range has already been added with gateway " + + otherVlanGateway + " ,and netmask " + otherVlanNetmask + + ", Please specify the gateway/netmask if you want to extend ip range" ); + } + if (!NetUtils.is31PrefixCidr(newCidr) && NetUtils.ipRangesOverlap(startIP, endIP, otherVlanStartIP, otherVlanEndIP)) { + throw new InvalidParameterValueException("The IP range already has IPs that overlap with the new range." + + " Please specify a different start IP/end IP."); + } + } else { + // For tagged or non-overlapping URIs we need to ensure there is no Public traffic type + boolean overlapped = false; + if (network.getTrafficType() == TrafficType.Public) { + overlapped = true; + } else { + final Long nwId = vlan.getNetworkId(); + if (nwId != null) { + final Network nw = _networkModel.getNetwork(nwId); + if (nw != null && nw.getTrafficType() == TrafficType.Public) { + overlapped = true; + } + } + + } + if (overlapped) { + throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag() + + " in zone " + zone.getName() + + " has overlapped with the subnet. Please specify a different gateway/netmask."); + } + } + } + } + private VlanVO commitVlanAndIpRange(final long zoneId, final long networkId, final long physicalNetworkId, final Long podId, final String startIP, final String endIP, final String vlanGateway, final String vlanNetmask, final String vlanId, final Domain domain, final Account vlanOwner, final String vlanIp6Gateway, final String vlanIp6Cidr, final boolean ipv4, final DataCenterVO zone, final VlanType vlanType, final String ipv6Range, final String ipRange, final boolean forSystemVms) { @@ -4770,7 +4776,7 @@ public boolean deleteVlanAndPublicIpRange(final long userId, final long vlanDbId @Override public void doInTransactionWithoutResult(final TransactionStatus status) { _publicIpAddressDao.deletePublicIPRange(vlanDbId); - s_logger.debug(String.format("Delete Public IP Range (from user_ip_address, where vlan_db_d=%s)", vlanDbId)); + s_logger.debug(String.format("Delete Public IP Range (from user_ip_address, where vlan_db_id=%s)", vlanDbId)); _vlanDao.remove(vlanDbId); s_logger.debug(String.format("Mark vlan as Remove vlan (vlan_db_id=%s)", vlanDbId)); @@ -5777,14 +5783,6 @@ public NetworkOfferingVO createNetworkOffering(final String name, final String d final int multicastRate = multicastRateStr == null ? 10 : Integer.parseInt(multicastRateStr); tags = com.cloud.utils.StringUtils.cleanupTags(tags); - // specifyVlan should always be true for Shared network offerings - if (!specifyVlan && type == GuestType.Shared) { - Set connectivityProviders = serviceProviderMap != null ? serviceProviderMap.get(Service.Connectivity) : null; - if (CollectionUtils.isEmpty(connectivityProviders) || !_networkModel.providerSupportsCapability(connectivityProviders, Service.Connectivity, Capability.NoVlan)) { - throw new InvalidParameterValueException("SpecifyVlan should be true if network offering's type is " + type); - } - } - // specifyIpRanges should always be true for Shared networks // specifyIpRanges can only be true for Isolated networks with no Source // Nat service diff --git a/server/src/main/java/com/cloud/network/NetworkModelImpl.java b/server/src/main/java/com/cloud/network/NetworkModelImpl.java index d4a93561e7b3..6986c51973c6 100644 --- a/server/src/main/java/com/cloud/network/NetworkModelImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkModelImpl.java @@ -74,6 +74,8 @@ import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.NetworkAccountDao; +import com.cloud.network.dao.NetworkAccountVO; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkDomainDao; import com.cloud.network.dao.NetworkDomainVO; @@ -179,6 +181,8 @@ public void setNetworkElements(List networkElements) { this.networkElements = networkElements; } + @Inject + NetworkAccountDao _networkAccountDao; @Inject NetworkDomainDao _networkDomainDao; @Inject @@ -217,7 +221,6 @@ public void setNetworkElements(List networkElements) { private NetworkService _networkService; private final HashMap _systemNetworks = new HashMap(5); - static Long s_privateOfferingId = null; SearchBuilder IpAddressSearch; SearchBuilder NicForTrafficTypeSearch; @@ -1659,30 +1662,13 @@ public void checkNetworkPermissions(Account owner, Network network) { throw new CloudRuntimeException("cannot check permissions on (Network) "); } // Perform account permission check - if ((network.getGuestType() != GuestType.Shared && network.getGuestType() != GuestType.L2) || - (network.getGuestType() == GuestType.Shared && network.getAclType() == ACLType.Account)) { + if (network.getGuestType() != GuestType.Shared || network.getAclType() == ACLType.Account) { AccountVO networkOwner = _accountDao.findById(network.getAccountId()); if (networkOwner == null) throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO)network).getUuid() + ", network does not have an owner"); if (owner.getType() != Account.ACCOUNT_TYPE_PROJECT && networkOwner.getType() == Account.ACCOUNT_TYPE_PROJECT) { - User user = CallContext.current().getCallingUser(); - Project project = projectDao.findByProjectAccountId(network.getAccountId()); - if (project == null) { - throw new CloudRuntimeException("Unable to find project to which the network belongs to"); - } - ProjectAccount projectAccountUser = _projectAccountDao.findByProjectIdUserId(project.getId(), user.getAccountId(), user.getId()); - if (projectAccountUser != null) { - if (!_projectAccountDao.canUserAccessProjectAccount(user.getAccountId(), user.getId(), network.getAccountId())) { - throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO)network).getUuid() + - ", permission denied"); - } - } else { - if (!_projectAccountDao.canAccessProjectAccount(owner.getAccountId(), network.getAccountId())) { - throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO) network).getUuid() + - ", permission denied"); - } - } + checkProjectNetworkPermissions(owner, networkOwner, network); } else { List networkMap = _networksDao.listBy(owner.getId(), network.getId()); if (networkMap == null || networkMap.isEmpty()) { @@ -1703,6 +1689,113 @@ public void checkNetworkPermissions(Account owner, Network network) { } } + private void checkProjectNetworkPermissions(Account owner, Account networkOwner, Network network){ + User user = CallContext.current().getCallingUser(); + Project project = projectDao.findByProjectAccountId(networkOwner.getId()); + if (project == null) { + throw new CloudRuntimeException("Unable to find project to which the network belongs to"); + } + ProjectAccount projectAccountUser = _projectAccountDao.findByProjectIdUserId(project.getId(), user.getAccountId(), user.getId()); + if (projectAccountUser != null) { + if (!_projectAccountDao.canUserAccessProjectAccount(user.getAccountId(), user.getId(), networkOwner.getId())) { + throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO)network).getUuid() + + ", permission denied"); + } + } else { + if (!_projectAccountDao.canAccessProjectAccount(owner.getAccountId(), networkOwner.getId())) { + throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO) network).getUuid() + + ", permission denied"); + } + } + } + + @Override + public void checkNetworkOperatePermissions(Account owner, Network network) { + if (network == null) { + throw new CloudRuntimeException("cannot check permissions on (Network) "); + } + if (network.getGuestType() == GuestType.Shared) { + checkSharedNetworkOperatePermissions(owner, network); + } else { + checkNonSharedNetworkOperatePermissions(owner, network); + } + } + + private void checkNonSharedNetworkOperatePermissions(Account owner, Network network) { + // check on isolated/L2 networks + if (owner.getType() == Account.ACCOUNT_TYPE_ADMIN) { + return; + } + Account networkOwner = _accountDao.findByIdIncludingRemoved(network.getAccountId()); + if (owner.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) { + if (!_domainDao.isChildDomain(owner.getDomainId(), networkOwner.getDomainId())) { + throw new PermissionDeniedException(String.format("network %s cannot be operated by domain admin %s", network, owner)); + } + } else if (owner.getType() == Account.ACCOUNT_TYPE_NORMAL) { + if (owner.getType() != Account.ACCOUNT_TYPE_PROJECT && networkOwner.getType() == Account.ACCOUNT_TYPE_PROJECT) { + checkProjectNetworkPermissions(owner, networkOwner, network); + } else if (networkOwner.getAccountId() != owner.getAccountId()) { + throw new PermissionDeniedException(String.format("network %s cannot be operated by normal user %s", network, owner)); + } + } else { + throw new PermissionDeniedException(String.format("network %s cannot be operated by this account %s", network, owner)); + } + } + + private void checkSharedNetworkOperatePermissions(Account owner, Network network) { + NetworkOffering networkOffering = _networkOfferingDao.findById(network.getNetworkOfferingId()); + if (networkOffering.isSpecifyVlan() && owner.getType() != Account.ACCOUNT_TYPE_ADMIN) { + throw new PermissionDeniedException(String.format("Shared network %s with specifyvlan=true can only be operated by root admin", network)); + } + if (owner.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) { + if (network.getAclType() == ACLType.Domain) { + // Allow domain admins to operate shared network for their domain. + Long networkDomainId = getDomainIdForSharedNetwork(network); + if (!_domainDao.isChildDomain(owner.getDomainId(), networkDomainId)) { + throw new PermissionDeniedException(String.format("Shared network %s belongs to another domain cannot be operated by domain admin %s", network, owner)); + } + } else if (network.getAclType() == ACLType.Account) { + // Allow domain admins to operate shared network for an account in their domain. + Long networkAccountId = getAccountIdForSharedNetwork(network); + if (!_domainDao.isChildDomain(owner.getDomainId(), _accountDao.findByIdIncludingRemoved(networkAccountId).getDomainId())) { + throw new PermissionDeniedException(String.format("Shared network %s belongs to an account in another domain cannot be operated by domain admin %s", network, owner)); + } + } + } else if (owner.getType() == Account.ACCOUNT_TYPE_NORMAL) { + // Allow normal users to operate shared network for themselves. + if (network.getAclType() == ACLType.Account) { + // Allow domain admin to operate shared network for an account in its domain. + Long networkAccountId = getAccountIdForSharedNetwork(network); + Account networkOwner = _accountDao.findByIdIncludingRemoved(networkAccountId); + if (owner.getType() != Account.ACCOUNT_TYPE_PROJECT && networkOwner.getType() == Account.ACCOUNT_TYPE_PROJECT) { + checkProjectNetworkPermissions(owner, networkOwner, network); + } else if (networkOwner.getAccountId() != owner.getAccountId()) { + throw new PermissionDeniedException(String.format("Shared network %s belongs to another account cannot be operated by normal user %s", network, owner)); + } + } else { + throw new PermissionDeniedException(String.format("Shared network %s belongs to domain cannot be operated by normal user %s", network, owner)); + } + } else if (owner.getType() != Account.ACCOUNT_TYPE_ADMIN) { + throw new PermissionDeniedException(String.format("Shared network %s cannot be operated by account %s with type = %d", network, owner, owner.getType())); + } + } + + private Long getAccountIdForSharedNetwork(Network network) { + NetworkAccountVO networkAccountMap = _networkAccountDao.getAccountNetworkMapByNetworkId(network.getId()); + if (networkAccountMap == null) { + throw new CloudRuntimeException(String.format("Cannot find account info for Shared network %s with aclType=Account", network)); + } + return networkAccountMap.getAccountId(); + } + + private Long getDomainIdForSharedNetwork(Network network) { + NetworkDomainVO networkDomainMap = _networkDomainDao.getDomainNetworkMapByNetworkId(network.getId()); + if (networkDomainMap == null) { + throw new CloudRuntimeException(String.format("Cannot find domain info for Shared network %s with aclType=Domain", network)); + } + return networkDomainMap.getDomainId(); + } + @Override public String getDefaultPublicTrafficLabel(long dcId, HypervisorType hypervisorType) { try { @@ -2090,10 +2183,12 @@ public boolean configure(String name, Map params) throws Configu NetworkOfferingVO storageNetworkOffering = new NetworkOfferingVO(NetworkOffering.SystemStorageNetwork, TrafficType.Storage, true); storageNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(storageNetworkOffering); _systemNetworks.put(NetworkOffering.SystemStorageNetwork, storageNetworkOffering); - NetworkOfferingVO privateGatewayNetworkOffering = new NetworkOfferingVO(NetworkOffering.SystemPrivateGatewayNetworkOffering, GuestType.Isolated); + NetworkOfferingVO privateGatewayNetworkOffering = new NetworkOfferingVO(NetworkOffering.SystemPrivateGatewayNetworkOffering, GuestType.Isolated, true); privateGatewayNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(privateGatewayNetworkOffering); _systemNetworks.put(NetworkOffering.SystemPrivateGatewayNetworkOffering, privateGatewayNetworkOffering); - s_privateOfferingId = privateGatewayNetworkOffering.getId(); + NetworkOfferingVO privateGatewayNetworkOfferingWithoutVlan = new NetworkOfferingVO(NetworkOffering.SystemPrivateGatewayNetworkOfferingWithoutVlan, GuestType.Isolated, false); + privateGatewayNetworkOfferingWithoutVlan = _networkOfferingDao.persistDefaultNetworkOffering(privateGatewayNetworkOfferingWithoutVlan); + _systemNetworks.put(NetworkOffering.SystemPrivateGatewayNetworkOfferingWithoutVlan, privateGatewayNetworkOfferingWithoutVlan); IpAddressSearch = _ipAddressDao.createSearchBuilder(); IpAddressSearch.and("accountId", IpAddressSearch.entity().getAllocatedToAccountId(), Op.EQ); diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java index f1bc68135385..4d4818b8c76e 100644 --- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java @@ -45,6 +45,8 @@ import org.apache.cloudstack.api.command.admin.network.CreateNetworkCmdByAdmin; import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd; import org.apache.cloudstack.api.command.admin.network.ListDedicatedGuestVlanRangesCmd; +import org.apache.cloudstack.api.command.admin.network.ListGuestVlansCmd; +import org.apache.cloudstack.api.command.admin.network.ListNetworksCmdByAdmin; import org.apache.cloudstack.api.command.admin.network.UpdateNetworkCmdByAdmin; import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; @@ -132,6 +134,7 @@ import com.cloud.network.element.OvsProviderVO; import com.cloud.network.element.VirtualRouterElement; import com.cloud.network.element.VpcVirtualRouterElement; +import com.cloud.network.guru.GuestNetworkGuru; import com.cloud.network.guru.NetworkGuru; import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.FirewallRuleVO; @@ -299,7 +302,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C @Inject InternalLoadBalancerElementService _internalLbElementSvc; @Inject - DataCenterVnetDao _datacneterVnet; + DataCenterVnetDao _dcVnetDao; @Inject AccountGuestVlanMapDao _accountGuestVlanMapDao; @Inject @@ -1116,6 +1119,7 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac String isolatedPvlan = cmd.getIsolatedPvlan(); String externalId = cmd.getExternalId(); String isolatedPvlanType = cmd.getIsolatedPvlanType(); + Long associatedNetworkId = cmd.getAssociatedNetworkId(); // Validate network offering NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId); @@ -1190,15 +1194,16 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac if (ntwkOff.getGuestType() == GuestType.Isolated || ntwkOff.getGuestType() == GuestType.L2) { aclType = ACLType.Account; } else if (ntwkOff.getGuestType() == GuestType.Shared) { - aclType = ACLType.Domain; + if (_accountMgr.isRootAdmin(caller.getId())) { + aclType = ACLType.Domain; + } else if (_accountMgr.isNormalUser(caller.getId())) { + aclType = ACLType.Account; + } else { + throw new InvalidParameterValueException("AclType must be specified for shared network created by domain admin"); + } } } - // Only Admin can create Shared networks - if ((ntwkOff.getGuestType() == GuestType.Shared) && !_accountMgr.isAdmin(caller.getId())) { - throw new InvalidParameterValueException("Only Admins can create network with guest type " + GuestType.Shared); - } - if (ntwkOff.getGuestType() != GuestType.Shared && (!StringUtils.isAllBlank(routerIp, routerIpv6))) { throw new InvalidParameterValueException("Router IP can be specified only for Shared networks"); } @@ -1339,12 +1344,16 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac validateRouterIps(routerIp, routerIpv6, startIP, endIP, gateway, netmask, startIPv6, endIPv6, ip6Cidr); - if (StringUtils.isNotBlank(isolatedPvlan) && (zone.getNetworkType() != NetworkType.Advanced || ntwkOff.getGuestType() == GuestType.Isolated)) { - throw new InvalidParameterValueException("Can only support create Private VLAN network with advanced shared or L2 network!"); - } - - if (StringUtils.isNotBlank(isolatedPvlan) && ipv6) { - throw new InvalidParameterValueException("Can only support create Private VLAN network with IPv4!"); + if (StringUtils.isNotBlank(isolatedPvlan)) { + if (!_accountMgr.isRootAdmin(caller.getId())) { + throw new InvalidParameterValueException("Only ROOT admin is allowed to create Private VLAN network"); + } + if (zone.getNetworkType() != NetworkType.Advanced || ntwkOff.getGuestType() == GuestType.Isolated) { + throw new InvalidParameterValueException("Can only support create Private VLAN network with advanced shared or L2 network!"); + } + if (ipv6) { + throw new InvalidParameterValueException("Can only support create Private VLAN network with IPv4!"); + } } Pair pvlanPair = getPrivateVlanPair(isolatedPvlan, isolatedPvlanType, vlanId); @@ -1357,12 +1366,27 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac performBasicPrivateVlanChecks(vlanId, secondaryVlanId, privateVlanType); - // Regular user can create Guest Isolated Source Nat enabled network only - if (_accountMgr.isNormalUser(caller.getId()) && (ntwkOff.getTrafficType() != TrafficType.Guest - || ntwkOff.getGuestType() != Network.GuestType.Isolated && areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) { - throw new InvalidParameterValueException( - String.format("Regular users can only create a network from network offerings having traffic type [%s] and network type [%s] with a service [%s] enabled.", TrafficType.Guest, - Network.GuestType.Isolated, Service.SourceNat.getName())); + // Regular user can create Guest Isolated Source Nat enabled network only, or a Shared network with specifyVlan=false + if (_accountMgr.isNormalUser(caller.getId())) { + if (ntwkOff.getTrafficType() != TrafficType.Guest) { + throw new InvalidParameterValueException("Regular users can only create a Guest network"); + } + if (ntwkOff.getGuestType() == GuestType.Isolated && areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)) { + s_logger.debug(String.format("Creating a network from network offerings having traffic type [%s] and network type [%s] with a service [%s] enabled.", + TrafficType.Guest, GuestType.Isolated, Service.SourceNat.getName())); + } else if (ntwkOff.getGuestType() == GuestType.Shared && ! ntwkOff.isSpecifyVlan()) { + s_logger.debug(String.format("Creating a network from network offerings having traffic type [%s] and network type [%s] with specifyVlan=%s.", + TrafficType.Guest, GuestType.Shared, ntwkOff.isSpecifyVlan())); + } else { + throw new InvalidParameterValueException( + String.format("Regular users can only create an %s network with a service [%s] enabled, or a %s network with specifyVlan=false.", + GuestType.Isolated, Service.SourceNat.getName(), GuestType.Shared)); + } + } + + // Ignore vlanId if it is passed but specifyvlan=false in network offering + if (ntwkOff.getGuestType() == GuestType.Shared && ! ntwkOff.isSpecifyVlan() && vlanId != null) { + throw new InvalidParameterValueException("Cannot specify vlanId when create a network from network offering with specifyvlan=false"); } // Don't allow to specify vlan if the caller is not ROOT admin @@ -1423,9 +1447,23 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac throwInvalidIdException("Network offering with specified id doesn't support adding multiple ip ranges", ntwkOff.getUuid(), "networkOfferingId"); } + Network associatedNetwork = null; + if (associatedNetworkId != null) { + if (vlanId != null) { + throw new InvalidParameterValueException("Associated network and vlanId are mutually exclusive"); + } + if (!_networkMgr.isSharedNetworkWithoutSpecifyVlan(ntwkOff)) { + throw new InvalidParameterValueException("Can only create Shared network with associated network if specifyVlan is false"); + } + associatedNetwork = implementAssociatedNetwork(associatedNetworkId, caller, owner, zone, + aclType == ACLType.Domain ? domainId : null, + aclType == ACLType.Account ? owner.getAccountId() : null, + cidr, startIP, endIP); + } + Network network = commitNetwork(networkOfferingId, gateway, startIP, endIP, netmask, networkDomain, vlanId, bypassVlanOverlapCheck, name, displayText, caller, physicalNetworkId, zoneId, domainId, isDomainSpecific, subdomainAccess, vpcId, startIPv6, endIPv6, ip6Gateway, ip6Cidr, displayNetwork, aclId, secondaryVlanId, privateVlanType, ntwkOff, pNtwk, aclType, owner, cidr, createVlan, - externalId, routerIp, routerIpv6); + externalId, routerIp, routerIpv6, associatedNetwork); if (hideIpAddressUsage) { _networkDetailsDao.persist(new NetworkDetailVO(network.getId(), Network.hideIpAddressUsage, String.valueOf(hideIpAddressUsage), false)); @@ -1433,25 +1471,70 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac // if the network offering has persistent set to true, implement the network if (ntwkOff.isPersistent()) { - try { - DeployDestination dest = new DeployDestination(zone, null, null, null); - UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId()); - Journal journal = new Journal.LogJournal("Implementing " + network, s_logger); - ReservationContext context = new ReservationContextImpl(UUID.randomUUID().toString(), journal, callerUser, caller); - s_logger.debug("Implementing network " + network + " as a part of network provision for persistent network"); - Pair implementedNetwork = _networkMgr.implementNetwork(network.getId(), dest, context); - if (implementedNetwork == null || implementedNetwork.first() == null) { - s_logger.warn("Failed to provision the network " + network); + return implementedNetworkInCreation(caller, zone, network); + } + return network; + } + + private Network implementAssociatedNetwork(Long associatedNetworkId, Account caller, Account owner, DataCenter zone, Long domainId, Long accountId, + String cidr, String startIp, String endIp) throws InsufficientCapacityException { + Network associatedNetwork = _networksDao.findById(associatedNetworkId); + if (associatedNetwork == null) { + throw new InvalidParameterValueException("Cannot find associated network with id = " + associatedNetworkId); + } + if (associatedNetwork.getGuestType() != GuestType.Isolated) { + throw new InvalidParameterValueException("Associated network MUST be an Isolated network"); + } + _accountMgr.checkAccess(caller, null, true, associatedNetwork); + if (accountId != null && associatedNetwork.getAccountId() != accountId) { + throw new InvalidParameterValueException("The new network and associated network MUST be owned by same account"); + } + if (domainId != null && associatedNetwork.getDomainId() != domainId) { + throw new InvalidParameterValueException("The new network and associated network MUST be in same domain"); + } + if (cidr != null && NetUtils.isNetworksOverlap(cidr, associatedNetwork.getCidr())) { + throw new InvalidParameterValueException("The cidr overlaps with associated network: " + associatedNetwork.getName()); + } + List associatedNetworks = _networkDetailsDao.findDetails(Network.AssociatedNetworkId, String.valueOf(associatedNetworkId), null); + for (NetworkDetailVO networkDetailVO : associatedNetworks) { + NetworkVO associatedNetwork2 = _networksDao.findById(networkDetailVO.getResourceId()); + if (associatedNetwork2 != null) { + List vlans = _vlanDao.listVlansByNetworkId(associatedNetwork2.getId()); + if (vlans.isEmpty()) { + continue; + } + String startIP2 = vlans.get(0).getIpRange().split("-")[0]; + String endIP2 = vlans.get(0).getIpRange().split("-")[1]; + if (StringUtils.isNoneBlank(startIp, startIP2) && NetUtils.ipRangesOverlap(startIp, endIp, startIP2, endIP2)) { + throw new InvalidParameterValueException("The startIp/endIp overlaps with network: " + associatedNetwork2.getName()); } - network = implementedNetwork.second(); - } catch (ResourceUnavailableException ex) { - s_logger.warn("Failed to implement persistent guest network " + network + "due to ", ex); - CloudRuntimeException e = new CloudRuntimeException("Failed to implement persistent guest network"); - e.addProxyObject(network.getUuid(), "networkId"); - throw e; } } - return network; + associatedNetwork = implementedNetworkInCreation(caller, zone, associatedNetwork); + if (associatedNetwork == null || associatedNetwork.getState() != Network.State.Implemented) { + throw new InvalidParameterValueException("Unable to implement associated network " + associatedNetwork); + } + return associatedNetwork; + } + + private Network implementedNetworkInCreation(final Account caller, final DataCenter zone, final Network network) throws InsufficientCapacityException { + try { + DeployDestination dest = new DeployDestination(zone, null, null, null); + UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId()); + Journal journal = new Journal.LogJournal("Implementing " + network, s_logger); + ReservationContext context = new ReservationContextImpl(UUID.randomUUID().toString(), journal, callerUser, caller); + s_logger.debug("Implementing network " + network + " as a part of network provision for persistent network"); + Pair implementedNetwork = _networkMgr.implementNetwork(network.getId(), dest, context); + if (implementedNetwork == null || implementedNetwork.first() == null) { + s_logger.warn("Failed to provision the network " + network); + } + return implementedNetwork.second(); + } catch (ResourceUnavailableException ex) { + s_logger.warn("Failed to implement persistent guest network " + network + "due to ", ex); + CloudRuntimeException e = new CloudRuntimeException("Failed to implement persistent guest network"); + e.addProxyObject(network.getUuid(), "networkId"); + throw e; + } } /** @@ -1497,11 +1580,12 @@ 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 vlanId, + private 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 NetworkOfferingVO ntwkOff, final PhysicalNetwork pNtwk, final ACLType aclType, final Account ownerFinal, - final String cidr, final boolean createVlan, final String externalId, String routerIp, String routerIpv6) throws InsufficientCapacityException, ResourceAllocationException { + final String cidr, final boolean createVlan, final String externalId, String routerIp, String routerIpv6, + final Network associatedNetwork) throws InsufficientCapacityException, ResourceAllocationException { try { Network network = Transaction.execute(new TransactionCallbackWithException() { @Override @@ -1524,6 +1608,20 @@ public Network doInTransaction(TransactionStatus status) throws InsufficientCapa owner = _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM); } + String vlanId = vlanIdFinal; + if (createVlan && vlanId == null && ntwkOff.getGuestType() == Network.GuestType.Shared && ! ntwkOff.isSpecifyVlan()) { + if (associatedNetwork != null) { + // Get vlanId from associated network + vlanId = associatedNetwork.getBroadcastUri().toString(); + } else { + // Allocate a vnet to shared network with specifyvlan=false + vlanId = _dcDao.allocateVnet(zoneId, physicalNetworkId, owner.getAccountId(), null, GuestNetworkGuru.UseSystemGuestVlans.valueIn(owner.getAccountId())); + if (vlanId == null) { + throw new InvalidParameterValueException("Cannot allocate a vnet for this Shared network"); + } + } + } + // Create guest network Network network = null; if (vpcId != null) { @@ -1559,11 +1657,14 @@ public Network doInTransaction(TransactionStatus status) throws InsufficientCapa zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, displayNetwork, isolatedPvlan, isolatedPvlanType, externalId, routerIp, routerIpv6); } - if (_accountMgr.isRootAdmin(caller.getId()) && createVlan && network != null) { + if (createVlan && network != null) { // Create vlan ip range _configMgr.createVlanAndPublicIpRange(pNtwk.getDataCenterId(), network.getId(), physicalNetworkId, false, false, null, startIP, endIP, gateway, netmask, vlanId, bypassVlanOverlapCheck, null, null, startIPv6, endIPv6, ip6Gateway, ip6Cidr); } + if (associatedNetwork != null) { + _networkDetailsDao.persist(new NetworkDetailVO(network.getId(), Network.AssociatedNetworkId, String.valueOf(associatedNetwork.getId()), true)); + } return network; } }); @@ -1612,6 +1713,12 @@ public Pair, Integer> searchForNetworks(ListNetworksCmd Boolean forVpc = cmd.getForVpc(); Boolean display = cmd.getDisplay(); Long networkOfferingId = cmd.getNetworkOfferingId(); + Long associatedNetworkId = cmd.getAssociatedNetworkId(); + + String vlanId = null; + if (cmd instanceof ListNetworksCmdByAdmin) { + vlanId = ((ListNetworksCmdByAdmin)cmd).getVlan(); + } // 1) default is system to false if not specified // 2) reset parameter to false if it's specified by a non-ROOT user @@ -1735,34 +1842,41 @@ public Pair, Integer> searchForNetworks(ListNetworksCmd sb.join("accountSearch", accountSearch, sb.entity().getAccountId(), accountSearch.entity().getId(), JoinBuilder.JoinType.INNER); + if (associatedNetworkId != null) { + SearchBuilder associatedNetworkSearch = _networkDetailsDao.createSearchBuilder(); + associatedNetworkSearch.and("name", associatedNetworkSearch.entity().getName(), SearchCriteria.Op.EQ); + associatedNetworkSearch.and("value", associatedNetworkSearch.entity().getValue(), SearchCriteria.Op.EQ); + sb.join("associatedNetworkSearch", associatedNetworkSearch, sb.entity().getId(), associatedNetworkSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER); + } + List networksToReturn = new ArrayList(); if (isSystem == null || !isSystem) { if (!permittedAccounts.isEmpty()) { //get account level networks networksToReturn.addAll(listAccountSpecificNetworks(buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, networkOfferingId, - aclType, skipProjectNetworks, restartRequired, specifyIpRanges, vpcId, tags, display), searchFilter, permittedAccounts)); + aclType, skipProjectNetworks, restartRequired, specifyIpRanges, vpcId, tags, display, vlanId, associatedNetworkId), searchFilter, permittedAccounts)); //get domain level networks if (domainId != null) { networksToReturn.addAll(listDomainLevelNetworks(buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, networkOfferingId, - aclType, true, restartRequired, specifyIpRanges, vpcId, tags, display), searchFilter, domainId, false)); + aclType, true, restartRequired, specifyIpRanges, vpcId, tags, display, vlanId, associatedNetworkId), searchFilter, domainId, false)); } } else { //add account specific networks networksToReturn.addAll(listAccountSpecificNetworksByDomainPath(buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, networkOfferingId, - aclType, skipProjectNetworks, restartRequired, specifyIpRanges, vpcId, tags, display), searchFilter, path, isRecursive)); + aclType, skipProjectNetworks, restartRequired, specifyIpRanges, vpcId, tags, display, vlanId, associatedNetworkId), searchFilter, path, isRecursive)); //add domain specific networks of domain + parent domains networksToReturn.addAll(listDomainSpecificNetworksByDomainPath(buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, networkOfferingId, - aclType, skipProjectNetworks, restartRequired, specifyIpRanges, vpcId, tags, display), searchFilter, path, isRecursive)); + aclType, skipProjectNetworks, restartRequired, specifyIpRanges, vpcId, tags, display, vlanId, associatedNetworkId), searchFilter, path, isRecursive)); //add networks of subdomains if (domainId == null) { networksToReturn.addAll(listDomainLevelNetworks(buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, networkOfferingId, - aclType, true, restartRequired, specifyIpRanges, vpcId, tags, display), searchFilter, caller.getDomainId(), true)); + aclType, true, restartRequired, specifyIpRanges, vpcId, tags, display, vlanId, associatedNetworkId), searchFilter, caller.getDomainId(), true)); } } } else { networksToReturn = _networksDao.search(buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, networkOfferingId, - null, skipProjectNetworks, restartRequired, specifyIpRanges, vpcId, tags, display), searchFilter); + null, skipProjectNetworks, restartRequired, specifyIpRanges, vpcId, tags, display, vlanId, associatedNetworkId), searchFilter); } if (supportedServicesStr != null && !supportedServicesStr.isEmpty() && !networksToReturn.isEmpty()) { @@ -1812,7 +1926,7 @@ public Pair, Integer> searchForNetworks(ListNetworksCmd private SearchCriteria buildNetworkSearchCriteria(SearchBuilder sb, String keyword, Long id, Boolean isSystem, Long zoneId, String guestIpType, String trafficType, Long physicalNetworkId, Long networkOfferingId, String aclType, boolean skipProjectNetworks, Boolean restartRequired, - Boolean specifyIpRanges, Long vpcId, Map tags, Boolean display) { + Boolean specifyIpRanges, Long vpcId, Map tags, Boolean display, String vlanId, Long associatedNetworkId) { SearchCriteria sc = sb.create(); @@ -1886,6 +2000,17 @@ private SearchCriteria buildNetworkSearchCriteria(SearchBuilder ssc = _networksDao.createSearchCriteria(); + ssc.addOr("broadcastUri", SearchCriteria.Op.EQ, vlanId); + ssc.addOr("broadcastUri", SearchCriteria.Op.LIKE, "%://" + vlanId); + sc.addAnd("broadcastUri", SearchCriteria.Op.SC, ssc); + } return sc; } @@ -1995,15 +2120,17 @@ public boolean deleteNetwork(long networkId, boolean forced) { throwInvalidIdException("Network with specified id is system and can't be removed", network.getUuid(), "networkId"); } - Account owner = _accountMgr.getAccount(network.getAccountId()); - - // Only Admin can delete Shared networks - if ((network.getGuestType() == GuestType.Shared) && !_accountMgr.isAdmin(caller.getId())) { - throw new InvalidParameterValueException("Only Admins can delete network with guest type " + network.getGuestType()); + List associatedNetworks = _networkDetailsDao.findDetails(Network.AssociatedNetworkId, String.valueOf(networkId), null); + for (NetworkDetailVO networkDetailVO : associatedNetworks) { + NetworkVO associatedNetwork = _networksDao.findById(networkDetailVO.getResourceId()); + if (associatedNetwork != null) { + String msg = String.format("Cannot delete network %s which is associated to another network %s", network.getUuid(), associatedNetwork.getUuid()); + s_logger.debug(msg); + throw new InvalidParameterValueException(msg); + } } - // Perform permission check - _accountMgr.checkAccess(caller, null, true, network); + Account owner = _accountMgr.getAccount(network.getAccountId()); if (forced && !_accountMgr.isRootAdmin(caller.getId())) { throw new InvalidParameterValueException("Delete network with 'forced' option can only be called by root admins"); @@ -3241,7 +3368,7 @@ public void addOrRemoveVnets(String[] listOfRanges, final PhysicalNetworkVO netw removeVnets = getVnetsToremove(network, vnetranges); //computing vnets to add - vnetsInDb.addAll(_datacneterVnet.listVnetsByPhysicalNetworkAndDataCenter(network.getDataCenterId(), network.getId())); + vnetsInDb.addAll(_dcVnetDao.listVnetsByPhysicalNetworkAndDataCenter(network.getDataCenterId(), network.getId())); tempVnets.addAll(vnetsInDb); for (Pair vlan : vnetranges) { for (i = vlan.first(); i <= vlan.second(); i++) { @@ -3283,7 +3410,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) { s_logger.debug("removing vnet range " + removeVnetsFinal.toString() + " for the physicalNetwork id= " + network.getId() + " and zone id=" + network.getDataCenterId() + " as a part of updatePhysicalNetwork call"); //deleteVnets takes a list of strings to be removed. each string is a vnet. - _datacneterVnet.deleteVnets(TransactionLegacy.currentTxn(), network.getDataCenterId(), network.getId(), removeVnetsFinal); + _dcVnetDao.deleteVnets(TransactionLegacy.currentTxn(), network.getDataCenterId(), network.getId(), removeVnetsFinal); } _physicalNetworkDao.update(network.getId(), network); } @@ -3319,7 +3446,7 @@ private List> validateVlanRange(PhysicalNetworkVO network // since adding a range adds each VNI to the database, need only check min/max for (String vnet : VnetRange) { s_logger.debug("Looking to see if VNI " + vnet + " already exists on another network in zone " + network.getDataCenterId()); - List vnis = _datacneterVnet.findVnet(network.getDataCenterId(), vnet); + List vnis = _dcVnetDao.findVnet(network.getDataCenterId(), vnet); if (vnis != null && !vnis.isEmpty()) { for (DataCenterVnetVO vni : vnis) { if (vni.getPhysicalNetworkId() != network.getId()) { @@ -3391,13 +3518,13 @@ private List getVnetsToremove(PhysicalNetworkVO network, List removeVnets = new ArrayList(); HashSet vnetsInDb = new HashSet(); - vnetsInDb.addAll(_datacneterVnet.listVnetsByPhysicalNetworkAndDataCenter(network.getDataCenterId(), network.getId())); + vnetsInDb.addAll(_dcVnetDao.listVnetsByPhysicalNetworkAndDataCenter(network.getDataCenterId(), network.getId())); //remove all the vnets from vnets in db to check if there are any vnets that are not there in given list. //remove all the vnets not in the list of vnets passed by the user. if (vnetRanges.size() == 0) { //this implies remove all vlans. removeVnets.addAll(vnetsInDb); - int allocated_vnets = _datacneterVnet.countAllocatedVnets(network.getId()); + int allocated_vnets = _dcVnetDao.countAllocatedVnets(network.getId()); if (allocated_vnets > 0) { throw new InvalidParameterValueException("physicalnetwork " + network.getId() + " has " + allocated_vnets + " vnets in use"); } @@ -3420,8 +3547,8 @@ private List getVnetsToremove(PhysicalNetworkVO network, List result = _datacneterVnet.listAllocatedVnetsInRange(network.getDataCenterId(), network.getId(), start, end); + _dcVnetDao.lockRange(network.getDataCenterId(), network.getId(), start, end); + List result = _dcVnetDao.listAllocatedVnetsInRange(network.getDataCenterId(), network.getId(), start, end); if (!result.isEmpty()) { throw new InvalidParameterValueException("physicalnetwork " + network.getId() + " has allocated vnets in the range " + start + "-" + end); @@ -3570,7 +3697,7 @@ protected void checkIfPhysicalNetworkIsDeletable(Long physicalNetworkId) { @Override @DB @ActionEvent(eventType = EventTypes.EVENT_GUEST_VLAN_RANGE_DEDICATE, eventDescription = "dedicating guest vlan range", async = false) - public GuestVlan dedicateGuestVlanRange(DedicateGuestVlanRangeCmd cmd) { + public GuestVlanRange dedicateGuestVlanRange(DedicateGuestVlanRangeCmd cmd) { String vlan = cmd.getVlan(); String accountName = cmd.getAccountName(); Long domainId = cmd.getDomainId(); @@ -3645,7 +3772,7 @@ public GuestVlan dedicateGuestVlanRange(DedicateGuestVlanRangeCmd cmd) { // Verify guest vlans in the range don't belong to a network of a different account for (int i = startVlan; i <= endVlan; i++) { - List allocatedVlans = _datacneterVnet.listAllocatedVnetsInRange(physicalNetwork.getDataCenterId(), physicalNetwork.getId(), startVlan, endVlan); + List allocatedVlans = _dcVnetDao.listAllocatedVnetsInRange(physicalNetwork.getDataCenterId(), physicalNetwork.getId(), startVlan, endVlan); if (allocatedVlans != null && !allocatedVlans.isEmpty()) { for (DataCenterVnetVO allocatedVlan : allocatedVlans) { if (allocatedVlan.getAccountId() != vlanOwner.getAccountId()) { @@ -3695,7 +3822,7 @@ public int compare(AccountGuestVlanMapVO obj1, AccountGuestVlanMapVO obj2) { List vlanTokens2 = getVlanFromRange(guestVlanMaps.get(i + 1).getGuestVlanRange()); // Range extends 2 vlan ranges, both to the right and left if (endVlan == (vlanTokens2.get(0).intValue() - 1) && guestVlanMaps.get(i + 1).getAccountId() == vlanOwnerId) { - _datacneterVnet.releaseDedicatedGuestVlans(guestVlanMaps.get(i + 1).getId()); + _dcVnetDao.releaseDedicatedGuestVlans(guestVlanMaps.get(i + 1).getId()); _accountGuestVlanMapDao.remove(guestVlanMaps.get(i + 1).getId()); updatedVlanRange = vlanTokens1.get(0).intValue() + "-" + vlanTokens2.get(1).intValue(); break; @@ -3719,9 +3846,9 @@ public int compare(AccountGuestVlanMapVO obj1, AccountGuestVlanMapVO obj2) { // For every guest vlan set the corresponding account guest vlan map id List finaVlanTokens = getVlanFromRange(accountGuestVlanMapVO.getGuestVlanRange()); for (int i = finaVlanTokens.get(0).intValue(); i <= finaVlanTokens.get(1).intValue(); i++) { - List dataCenterVnet = _datacneterVnet.findVnet(physicalNetwork.getDataCenterId(), physicalNetworkId, Integer.toString(i)); + List dataCenterVnet = _dcVnetDao.findVnet(physicalNetwork.getDataCenterId(), physicalNetworkId, Integer.toString(i)); dataCenterVnet.get(0).setAccountGuestVlanMapId(accountGuestVlanMapVO.getId()); - _datacneterVnet.update(dataCenterVnet.get(0).getId(), dataCenterVnet.get(0)); + _dcVnetDao.update(dataCenterVnet.get(0).getId(), dataCenterVnet.get(0)); } return accountGuestVlanMapVO; } @@ -3743,7 +3870,7 @@ private List getVlanFromRange(String vlanRange) { } @Override - public Pair, Integer> listDedicatedGuestVlanRanges(ListDedicatedGuestVlanRangesCmd cmd) { + public Pair, Integer> listDedicatedGuestVlanRanges(ListDedicatedGuestVlanRangesCmd cmd) { Long id = cmd.getId(); String accountName = cmd.getAccountName(); Long domainId = cmd.getDomainId(); @@ -3816,7 +3943,7 @@ public Pair, Integer> listDedicatedGuestVlanRanges(Lis Filter searchFilter = new Filter(AccountGuestVlanMapVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); Pair, Integer> result = _accountGuestVlanMapDao.searchAndCount(sc, searchFilter); - return new Pair, Integer>(result.first(), result.second()); + return new Pair, Integer>(result.first(), result.second()); } @Override @@ -3830,7 +3957,7 @@ public boolean releaseDedicatedGuestVlanRange(Long dedicatedGuestVlanRangeId) { } // Remove dedication for the guest vlan - _datacneterVnet.releaseDedicatedGuestVlans(dedicatedGuestVlan.getId()); + _dcVnetDao.releaseDedicatedGuestVlans(dedicatedGuestVlan.getId()); if (_accountGuestVlanMapDao.remove(dedicatedGuestVlanRangeId)) { return true; } else { @@ -4520,9 +4647,10 @@ public IpAddress associateIPToNetwork(long ipId, long networkId) @Override @DB public Network createPrivateNetwork(final String networkName, final String displayText, long physicalNetworkId, String broadcastUriString, final String startIp, String endIp, final String gateway, - String netmask, final long networkOwnerId, final Long vpcId, final Boolean sourceNat, final Long networkOfferingId, final Boolean bypassVlanOverlapCheck) + String netmask, final long networkOwnerId, final Long vpcId, final Boolean sourceNat, final Long networkOfferingId, final Boolean bypassVlanOverlapCheck, final Long associatedNetworkId) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { + final Account caller = CallContext.current().getCallingAccount(); final Account owner = _accountMgr.getAccount(networkOwnerId); // Get system network offering @@ -4560,13 +4688,22 @@ public Network createPrivateNetwork(final String networkName, final String displ final String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask); - URI uri = BroadcastDomainType.fromString(broadcastUriString); - final String uriString = uri.toString(); - BroadcastDomainType tiep = BroadcastDomainType.getSchemeValue(uri); - // numeric vlan or vlan uri are ok for now - // TODO make a test for any supported scheme - if (!(tiep == BroadcastDomainType.Vlan || tiep == BroadcastDomainType.Lswitch)) { - throw new InvalidParameterValueException("unsupported type of broadcastUri specified: " + broadcastUriString); + final String uriString; + if (broadcastUriString != null) { + URI uri = BroadcastDomainType.fromString(broadcastUriString); + uriString = uri.toString(); + BroadcastDomainType tiep = BroadcastDomainType.getSchemeValue(uri); + // numeric vlan or vlan uri are ok for now + // TODO make a test for any supported scheme + if (!(tiep == BroadcastDomainType.Vlan || tiep == BroadcastDomainType.Lswitch)) { + throw new InvalidParameterValueException("unsupported type of broadcastUri specified: " + broadcastUriString); + } + } else if (associatedNetworkId != null) { + DataCenter zone = _dcDao.findById(pNtwk.getDataCenterId()); + Network associatedNetwork = implementAssociatedNetwork(associatedNetworkId, caller, owner, zone, null, owner.getAccountId(), cidr, startIp, endIp); + uriString = associatedNetwork.getBroadcastUri().toString(); + } else { + throw new InvalidParameterValueException("One of uri and associatedNetworkId must be passed"); } final NetworkOfferingVO ntwkOffFinal = ntwkOff; @@ -4584,6 +4721,9 @@ public Network doInTransaction(TransactionStatus status) throws ResourceAllocati privateNetwork = _networkMgr.createPrivateNetwork(ntwkOffFinal.getId(), networkName, displayText, gateway, cidr, uriString, bypassVlanOverlapCheck, owner, pNtwk, vpcId); if (privateNetwork != null) { s_logger.debug("Successfully created guest network " + privateNetwork); + if (associatedNetworkId != null) { + _networkDetailsDao.persist(new NetworkDetailVO(privateNetwork.getId(), Network.AssociatedNetworkId, String.valueOf(associatedNetworkId), true)); + } } else { throw new CloudRuntimeException("Creating guest network failed"); } @@ -4732,6 +4872,42 @@ public boolean releasePodIp(ReleasePodIpCmdByAdmin ip) throws CloudRuntimeExcept return true; } + @Override + public Pair, Integer> listGuestVlans(ListGuestVlansCmd cmd) { + Long id = cmd.getId(); + Long zoneId = cmd.getZoneId(); + Long physicalNetworkId = cmd.getPhysicalNetworkId(); + String vnet = cmd.getVnet(); + Boolean allocatedOnly = cmd.getAllocatedOnly(); + String keyword = cmd.getKeyword(); + + SearchCriteria vlanSearch = _dcVnetDao.createSearchCriteria(); + if (id != null) { + vlanSearch.addAnd("id", Op.EQ, id); + } + if (zoneId != null) { + vlanSearch.addAnd("dataCenterId", Op.EQ, zoneId); + } + if (physicalNetworkId != null) { + vlanSearch.addAnd("physicalNetworkId", Op.EQ, physicalNetworkId); + } + if (vnet != null) { + vlanSearch.addAnd("vnet", Op.EQ, vnet); + } + if (allocatedOnly != null && allocatedOnly) { + vlanSearch.addAnd("takenAt", Op.NNULL); + } + if (keyword != null) { + vlanSearch.addAnd("vnet", Op.LIKE, "%" + vnet + "%"); + } + Long pageSizeVal = cmd.getPageSizeVal(); + Long startIndex = cmd.getStartIndex(); + Filter searchFilter = new Filter(DataCenterVnetVO.class, "vnet", true, startIndex, pageSizeVal); + + Pair, Integer> vlans = _dcVnetDao.searchAndCount(vlanSearch, searchFilter); + return new Pair, Integer>(vlans.first(), vlans.second()); + } + @Override public String getConfigComponentName() { return NetworkService.class.getSimpleName(); 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 c1ce99c0606e..a29afa775e9e 100644 --- a/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java @@ -112,7 +112,7 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur IpAddressManager _ipAddrMgr; Random _rand = new Random(System.currentTimeMillis()); - static final ConfigKey UseSystemGuestVlans = + public static final ConfigKey UseSystemGuestVlans = new ConfigKey( "Advanced", Boolean.class, diff --git a/server/src/main/java/com/cloud/network/guru/PrivateNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/PrivateNetworkGuru.java index 3b3dabe1da63..6e2f1db5f27a 100644 --- a/server/src/main/java/com/cloud/network/guru/PrivateNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/PrivateNetworkGuru.java @@ -124,7 +124,7 @@ public Network design(NetworkOffering offering, DeploymentPlan plan, Network use throw new InvalidParameterValueException("Can't design network " + network + "; netmask/gateway must be passed in"); } - if (offering.isSpecifyVlan()) { + if (userSpecified.getBroadcastUri() != null) { network.setBroadcastUri(userSpecified.getBroadcastUri()); network.setState(State.Setup); } 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 5752da2bf917..e1f25a9ffc08 100644 --- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java @@ -43,8 +43,10 @@ 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.CreateVPCOfferingCmd; import org.apache.cloudstack.api.command.admin.vpc.UpdateVPCOfferingCmd; +import org.apache.cloudstack.api.command.user.vpc.CreatePrivateGatewayCmd; import org.apache.cloudstack.api.command.user.vpc.ListPrivateGatewaysCmd; import org.apache.cloudstack.api.command.user.vpc.ListStaticRoutesCmd; import org.apache.cloudstack.api.command.user.vpc.ListVPCOfferingsCmd; @@ -115,6 +117,8 @@ import com.cloud.network.vpn.Site2SiteVpnManager; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.NetworkOfferingServiceMapVO; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import com.cloud.org.Grouping; import com.cloud.projects.Project.ListProjectResourcesCriteria; @@ -225,6 +229,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis DomainDao domainDao; @Inject private AnnotationDao annotationDao; + @Inject + NetworkOfferingDao _networkOfferingDao; @Inject private VpcPrivateGatewayTransactionCallable vpcTxCallable; @@ -1821,8 +1827,29 @@ protected PrivateGateway getPrivateGatewayProfile(final VpcGateway gateway) { @Override @DB @ActionEvent(eventType = EventTypes.EVENT_PRIVATE_GATEWAY_CREATE, eventDescription = "creating VPC private gateway", create = true) - public PrivateGateway createVpcPrivateGateway(final long vpcId, Long physicalNetworkId, final String broadcastUri, final String ipAddress, final String gateway, - final String netmask, final long gatewayOwnerId, final Long networkOfferingId, final Boolean isSourceNat, final Long aclId, final Boolean bypassVlanOverlapCheck) throws ResourceAllocationException, + public PrivateGateway createVpcPrivateGateway(CreatePrivateGatewayCmd command) throws ResourceAllocationException, + ConcurrentOperationException, InsufficientCapacityException { + long vpcId = command.getVpcId(); + String ipAddress = command.getIpAddress(); + String gateway = command.getGateway(); + String netmask = command.getNetmask(); + long gatewayOwnerId = command.getEntityOwnerId(); + Long networkOfferingId = command.getNetworkOfferingId(); + Boolean isSourceNat = command.getIsSourceNat(); + Long aclId = command.getAclId(); + Long associatedNetworkId = command.getAssociatedNetworkId(); + + if (command instanceof CreatePrivateGatewayByAdminCmd) { + Long physicalNetworkId = ((CreatePrivateGatewayByAdminCmd)command).getPhysicalNetworkId(); + String broadcastUri = ((CreatePrivateGatewayByAdminCmd)command).getBroadcastUri(); + Boolean bypassVlanOverlapCheck = ((CreatePrivateGatewayByAdminCmd)command).getBypassVlanOverlapCheck(); + return createVpcPrivateGateway(vpcId, physicalNetworkId, broadcastUri, ipAddress, gateway, netmask, gatewayOwnerId, networkOfferingId, isSourceNat, aclId, bypassVlanOverlapCheck, associatedNetworkId); + } + return createVpcPrivateGateway(vpcId, null, null, ipAddress, gateway, netmask, gatewayOwnerId, networkOfferingId, isSourceNat, aclId, false, associatedNetworkId); + } + + private PrivateGateway createVpcPrivateGateway(final long vpcId, Long physicalNetworkId, final String broadcastUri, final String ipAddress, final String gateway, + final String netmask, final long gatewayOwnerId, final Long networkOfferingIdPassed, final Boolean isSourceNat, final Long aclId, final Boolean bypassVlanOverlapCheck, final Long associatedNetworkId) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { // Validate parameters @@ -1833,8 +1860,63 @@ public PrivateGateway createVpcPrivateGateway(final long vpcId, Long physicalNet throw ex; } + // Validate vlanId and associatedNetworkId + if (broadcastUri == null && associatedNetworkId == null) { + throw new InvalidParameterValueException("One of vlanId and associatedNetworkId must be specified"); + } + if (broadcastUri != null && associatedNetworkId != null) { + throw new InvalidParameterValueException("vlanId and associatedNetworkId are mutually exclusive"); + } + + // Validate network offering + NetworkOfferingVO ntwkOff = null; + if (networkOfferingIdPassed != null) { + ntwkOff = _networkOfferingDao.findById(networkOfferingIdPassed); + if (ntwkOff == null) { + throw new InvalidParameterValueException("Unable to find network offering by id specified"); + } + if (! TrafficType.Guest.equals(ntwkOff.getTrafficType())) { + throw new InvalidParameterValueException("The network offering cannot be used to create Guest network"); + } + if (! GuestType.Isolated.equals(ntwkOff.getGuestType())) { + throw new InvalidParameterValueException("The network offering cannot be used to create Isolated network"); + } + } else if (broadcastUri != null) { + ntwkOff = _networkOfferingDao.findByUniqueName(NetworkOffering.SystemPrivateGatewayNetworkOffering); + } else { + ntwkOff = _networkOfferingDao.findByUniqueName(NetworkOffering.SystemPrivateGatewayNetworkOfferingWithoutVlan); + } + final Long networkOfferingId = ntwkOff.getId(); + + Account caller = CallContext.current().getCallingAccount(); + if (!_accountMgr.isRootAdmin(caller.getId()) && (ntwkOff.isSpecifyVlan() || broadcastUri != null || bypassVlanOverlapCheck)) { + throw new InvalidParameterValueException("Only ROOT admin is allowed to specify vlanId or bypass vlan overlap check"); + } + if (ntwkOff.isSpecifyVlan() && broadcastUri == null) { + throw new InvalidParameterValueException("vlanId must be specified for this network offering"); + } + if (! ntwkOff.isSpecifyVlan() && associatedNetworkId == null) { + throw new InvalidParameterValueException("associatedNetworkId must be specified for this network offering"); + } + + final Long dcId = vpc.getZoneId(); PhysicalNetwork physNet = null; // Validate physical network + if (associatedNetworkId != null) { + Network associatedNetwork = _entityMgr.findById(Network.class, associatedNetworkId); + if (associatedNetwork == null) { + throw new InvalidParameterValueException("Unable to find network by ID " + associatedNetworkId); + } + if (physicalNetworkId != null && !physicalNetworkId.equals(associatedNetwork.getPhysicalNetworkId())) { + throw new InvalidParameterValueException("The network can only be created on the same physical network as the associated network"); + } else if (physicalNetworkId == null) { + physicalNetworkId = associatedNetwork.getPhysicalNetworkId(); + } + } + if (physicalNetworkId == null) { + // Determine the physical network by network offering tags + physicalNetworkId = _ntwkSvc.findPhysicalNetworkId(dcId, ntwkOff.getTags(), ntwkOff.getTrafficType()); + } if (physicalNetworkId == null) { final List pNtwks = _ntwkModel.getPhysicalNtwksSupportingTrafficType(vpc.getZoneId(), TrafficType.Guest); if (pNtwks.isEmpty() || pNtwks.size() != 1) { @@ -1847,91 +1929,83 @@ public PrivateGateway createVpcPrivateGateway(final long vpcId, Long physicalNet if (physNet == null) { physNet = _entityMgr.findById(PhysicalNetwork.class, physicalNetworkId); } - final Long dcId = physNet.getDataCenterId(); final Long physicalNetworkIdFinal = physicalNetworkId; final PhysicalNetwork physNetFinal = physNet; VpcGatewayVO gatewayVO = null; try { - gatewayVO = Transaction.execute(new TransactionCallbackWithException() { - @Override - public VpcGatewayVO doInTransaction(final TransactionStatus status) throws ResourceAllocationException, ConcurrentOperationException, - InsufficientCapacityException { - s_logger.debug("Creating Private gateway for VPC " + vpc); - // 1) create private network unless it is existing and - // lswitch'd - Network privateNtwk = null; - if (BroadcastDomainType.getSchemeValue(BroadcastDomainType.fromString(broadcastUri)) == BroadcastDomainType.Lswitch) { - final String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask); - privateNtwk = _ntwkDao.getPrivateNetwork(broadcastUri, cidr, gatewayOwnerId, dcId, networkOfferingId, vpcId); - // if the dcid is different we get no network so next we - // try to create it - } - if (privateNtwk == null) { - s_logger.info("creating new network for vpc " + vpc + " using broadcast uri: " + broadcastUri); - final String networkName = "vpc-" + vpc.getName() + "-privateNetwork"; - privateNtwk = _ntwkSvc.createPrivateNetwork(networkName, networkName, physicalNetworkIdFinal, broadcastUri, ipAddress, null, gateway, netmask, - gatewayOwnerId, vpcId, isSourceNat, networkOfferingId, bypassVlanOverlapCheck); - } else { // create the nic/ip as createPrivateNetwork - // doesn''t do that work for us now - s_logger.info("found and using existing network for vpc " + vpc + ": " + broadcastUri); - final DataCenterVO dc = _dcDao.lockRow(physNetFinal.getDataCenterId(), true); - - // add entry to private_ip_address table - PrivateIpVO privateIp = _privateIpDao.findByIpAndSourceNetworkId(privateNtwk.getId(), ipAddress); - if (privateIp != null) { - throw new InvalidParameterValueException("Private ip address " + ipAddress + " already used for private gateway" + " in zone " - + _entityMgr.findById(DataCenter.class, dcId).getName()); - } - - final Long mac = dc.getMacAddress(); - final Long nextMac = mac + 1; - dc.setMacAddress(nextMac); + s_logger.debug("Creating Private gateway for VPC " + vpc); + // 1) create private network unless it is existing and + // lswitch'd + Network privateNtwk = null; + if (broadcastUri != null + && BroadcastDomainType.getSchemeValue(BroadcastDomainType.fromString(broadcastUri)) == BroadcastDomainType.Lswitch) { + final String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask); + privateNtwk = _ntwkDao.getPrivateNetwork(broadcastUri, cidr, gatewayOwnerId, dcId, networkOfferingId, vpcId); + // if the dcid is different we get no network so next we + // try to create it + } + if (privateNtwk == null) { + s_logger.info("creating new network for vpc " + vpc + " using broadcast uri: " + broadcastUri + " and associated network id: " + associatedNetworkId); + final String networkName = "vpc-" + vpc.getName() + "-privateNetwork"; + privateNtwk = _ntwkSvc.createPrivateNetwork(networkName, networkName, physicalNetworkIdFinal, broadcastUri, ipAddress, null, gateway, netmask, + gatewayOwnerId, vpcId, isSourceNat, networkOfferingId, bypassVlanOverlapCheck, associatedNetworkId); + } else { // create the nic/ip as createPrivateNetwork + // doesn''t do that work for us now + s_logger.info("found and using existing network for vpc " + vpc + ": " + broadcastUri); + final DataCenterVO dc = _dcDao.lockRow(physNetFinal.getDataCenterId(), true); + + // add entry to private_ip_address table + PrivateIpVO privateIp = _privateIpDao.findByIpAndSourceNetworkId(privateNtwk.getId(), ipAddress); + if (privateIp != null) { + throw new InvalidParameterValueException("Private ip address " + ipAddress + " already used for private gateway" + " in zone " + + _entityMgr.findById(DataCenter.class, dcId).getName()); + } - s_logger.info("creating private ip address for vpc (" + ipAddress + ", " + privateNtwk.getId() + ", " + nextMac + ", " + vpcId + ", " + isSourceNat + ")"); - privateIp = new PrivateIpVO(ipAddress, privateNtwk.getId(), nextMac, vpcId, isSourceNat); - _privateIpDao.persist(privateIp); + final Long mac = dc.getMacAddress(); + final Long nextMac = mac + 1; + dc.setMacAddress(nextMac); - _dcDao.update(dc.getId(), dc); - } + s_logger.info("creating private ip address for vpc (" + ipAddress + ", " + privateNtwk.getId() + ", " + nextMac + ", " + vpcId + ", " + isSourceNat + ")"); + privateIp = new PrivateIpVO(ipAddress, privateNtwk.getId(), nextMac, vpcId, isSourceNat); + _privateIpDao.persist(privateIp); - long networkAclId = NetworkACL.DEFAULT_DENY; - if (aclId != null) { - final NetworkACLVO aclVO = _networkAclDao.findById(aclId); - if (aclVO == null) { - throw new InvalidParameterValueException("Invalid network acl id passed "); - } - if (aclVO.getVpcId() != vpcId && !(aclId == NetworkACL.DEFAULT_DENY || aclId == NetworkACL.DEFAULT_ALLOW)) { - throw new InvalidParameterValueException("Private gateway and network acl are not in the same vpc"); - } + _dcDao.update(dc.getId(), dc); + } - networkAclId = aclId; - } + long networkAclId = NetworkACL.DEFAULT_DENY; + if (aclId != null) { + final NetworkACLVO aclVO = _networkAclDao.findById(aclId); + if (aclVO == null) { + throw new InvalidParameterValueException("Invalid network acl id passed "); + } + if (aclVO.getVpcId() != vpcId && !(aclId == NetworkACL.DEFAULT_DENY || aclId == NetworkACL.DEFAULT_ALLOW)) { + throw new InvalidParameterValueException("Private gateway and network acl are not in the same vpc"); + } - { // experimental block, this is a hack - // set vpc id in network to null - // might be needed for all types of broadcast domains - // the ugly hack is that vpc gateway nets are created as - // guest network - // while they are not. - // A more permanent solution would be to define a type of - // 'gatewaynetwork' - // so that handling code is not mixed between the two - final NetworkVO gatewaynet = _ntwkDao.findById(privateNtwk.getId()); - gatewaynet.setVpcId(null); - _ntwkDao.persist(gatewaynet); - } + networkAclId = aclId; + } - // 2) create gateway entry - final VpcGatewayVO gatewayVO = new VpcGatewayVO(ipAddress, VpcGateway.Type.Private, vpcId, privateNtwk.getDataCenterId(), privateNtwk.getId(), broadcastUri, - gateway, netmask, vpc.getAccountId(), vpc.getDomainId(), isSourceNat, networkAclId); - _vpcGatewayDao.persist(gatewayVO); + { // experimental block, this is a hack + // set vpc id in network to null + // might be needed for all types of broadcast domains + // the ugly hack is that vpc gateway nets are created as + // guest network + // while they are not. + // A more permanent solution would be to define a type of + // 'gatewaynetwork' + // so that handling code is not mixed between the two + final NetworkVO gatewaynet = _ntwkDao.findById(privateNtwk.getId()); + gatewaynet.setVpcId(null); + _ntwkDao.persist(gatewaynet); + } - s_logger.debug("Created vpc gateway entry " + gatewayVO); + // 2) create gateway entry + gatewayVO = new VpcGatewayVO(ipAddress, VpcGateway.Type.Private, vpcId, privateNtwk.getDataCenterId(), privateNtwk.getId(), privateNtwk.getBroadcastUri().toString(), + gateway, netmask, vpc.getAccountId(), vpc.getDomainId(), isSourceNat, networkAclId); + _vpcGatewayDao.persist(gatewayVO); - return gatewayVO; - } - }); + s_logger.debug("Created vpc gateway entry " + gatewayVO); } catch (final Exception e) { ExceptionUtil.rethrowRuntime(e); ExceptionUtil.rethrow(e, InsufficientCapacityException.class); @@ -2005,6 +2079,17 @@ public boolean deleteVpcPrivateGateway(final long gatewayId) throws ConcurrentOp throw new ConcurrentOperationException("Unable to lock gateway " + gatewayId); } + final Account caller = CallContext.current().getCallingAccount(); + if (!_accountMgr.isRootAdmin(caller.getId())) { + _accountMgr.checkAccess(caller, null, false, gatewayVO); + final NetworkVO networkVO = _ntwkDao.findById(gatewayVO.getNetworkId()); + if (networkVO != null) { + _accountMgr.checkAccess(caller, null, false, networkVO); + if (_networkOfferingDao.findById(networkVO.getNetworkOfferingId()).isSpecifyVlan()) { + throw new InvalidParameterValueException("Unable to delete private gateway with specified vlan by non-ROOT accounts"); + } + } + } try { Transaction.execute(new TransactionCallbackNoReturn() { @Override diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java index 33c0c2197df0..17b0000f1e61 100644 --- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java @@ -962,8 +962,10 @@ protected void createDefaultNetworkOfferings() { controlNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(controlNetworkOffering); NetworkOfferingVO storageNetworkOffering = new NetworkOfferingVO(NetworkOffering.SystemStorageNetwork, TrafficType.Storage, true); storageNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(storageNetworkOffering); - NetworkOfferingVO privateGatewayNetworkOffering = new NetworkOfferingVO(NetworkOffering.SystemPrivateGatewayNetworkOffering, GuestType.Isolated); + NetworkOfferingVO privateGatewayNetworkOffering = new NetworkOfferingVO(NetworkOffering.SystemPrivateGatewayNetworkOffering, GuestType.Isolated, true); privateGatewayNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(privateGatewayNetworkOffering); + NetworkOfferingVO privateGatewayNetworkOfferingWithoutVlan = new NetworkOfferingVO(NetworkOffering.SystemPrivateGatewayNetworkOfferingWithoutVlan, GuestType.Isolated, false); + privateGatewayNetworkOfferingWithoutVlan = _networkOfferingDao.persistDefaultNetworkOffering(privateGatewayNetworkOfferingWithoutVlan); //populate providers final Map defaultSharedNetworkOfferingProviders = new HashMap(); diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 08ae0ad13f43..d13119bbd2b6 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -134,6 +134,7 @@ import org.apache.cloudstack.api.command.admin.network.DeletePhysicalNetworkCmd; import org.apache.cloudstack.api.command.admin.network.DeleteStorageNetworkIpRangeCmd; import org.apache.cloudstack.api.command.admin.network.ListDedicatedGuestVlanRangesCmd; +import org.apache.cloudstack.api.command.admin.network.ListGuestVlansCmd; import org.apache.cloudstack.api.command.admin.network.ListNetworkDeviceCmd; import org.apache.cloudstack.api.command.admin.network.ListNetworkIsolationMethodsCmd; import org.apache.cloudstack.api.command.admin.network.ListNetworkServiceProvidersCmd; @@ -296,7 +297,7 @@ import org.apache.cloudstack.api.command.admin.volume.ResizeVolumeCmdByAdmin; import org.apache.cloudstack.api.command.admin.volume.UpdateVolumeCmdByAdmin; import org.apache.cloudstack.api.command.admin.volume.UploadVolumeCmdByAdmin; -import org.apache.cloudstack.api.command.admin.vpc.CreatePrivateGatewayCmd; +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.DeletePrivateGatewayCmd; @@ -522,6 +523,7 @@ import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; import org.apache.cloudstack.api.command.user.volume.UpdateVolumeCmd; import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd; +import org.apache.cloudstack.api.command.user.vpc.CreatePrivateGatewayCmd; import org.apache.cloudstack.api.command.user.vpc.CreateStaticRouteCmd; import org.apache.cloudstack.api.command.user.vpc.CreateVPCCmd; import org.apache.cloudstack.api.command.user.vpc.DeleteStaticRouteCmd; @@ -3466,6 +3468,7 @@ public List> getCommands() { cmdList.add(CreateVPCCmdByAdmin.class); cmdList.add(ListVPCsCmdByAdmin.class); cmdList.add(UpdateVPCCmdByAdmin.class); + cmdList.add(CreatePrivateGatewayByAdminCmd.class); cmdList.add(UpdateLBStickinessPolicyCmd.class); cmdList.add(UpdateLBHealthCheckPolicyCmd.class); cmdList.add(GetUploadParamsForTemplateCmd.class); @@ -3486,6 +3489,7 @@ public List> getCommands() { cmdList.add(UploadResourceIconCmd.class); cmdList.add(DeleteResourceIconCmd.class); cmdList.add(ListResourceIconCmd.class); + cmdList.add(ListGuestVlansCmd.class); // Out-of-band management APIs for admins cmdList.add(EnableOutOfBandManagementForHostCmd.class); diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index 88cd217698a3..377a2eca0f5a 100644 --- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java +++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java @@ -21,6 +21,7 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -533,7 +534,8 @@ public void checkAccess(Account caller, AccessType accessType, boolean sameOwner Account account = ApiDBUtils.findAccountById(entity.getAccountId()); domainId = account != null ? account.getDomainId() : -1; } - if (entity.getAccountId() != -1 && domainId != -1 && !(entity instanceof VirtualMachineTemplate) && !(entity instanceof Network && accessType != null && accessType == AccessType.UseEntry) + if (entity.getAccountId() != -1 && domainId != -1 && !(entity instanceof VirtualMachineTemplate) + && !(entity instanceof Network && accessType != null && (accessType == AccessType.UseEntry || accessType == AccessType.OperateEntry)) && !(entity instanceof AffinityGroup)) { List toBeChecked = domains.get(entity.getDomainId()); // for templates, we don't have to do cross domains check @@ -817,6 +819,15 @@ protected boolean cleanupAccount(AccountVO account, long callerUserId, Account c s_logger.debug("Deleting networks for account " + account.getId()); List networks = _networkDao.listByOwner(accountId); if (networks != null) { + Collections.sort(networks, new Comparator() { + @Override + public int compare(NetworkVO network1, NetworkVO network2) { + if (network1.getGuestType() != network2.getGuestType() && Network.GuestType.Isolated.equals(network2.getGuestType())) { + return -1; + }; + return 1; + } + }); for (NetworkVO network : networks) { ReservationContext context = new ReservationContextImpl(null, null, getActiveUser(callerUserId), caller); diff --git a/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java b/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java index 04e3e634f096..7307f665b09f 100644 --- a/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java +++ b/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java @@ -30,6 +30,7 @@ import java.util.UUID; import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.log4j.Logger; import org.junit.Before; @@ -58,6 +59,8 @@ import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserVO; import com.cloud.utils.db.DB; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; @@ -101,6 +104,9 @@ public void setup() throws Exception { Account account = new AccountVO("testaccount", 1, "networkdomain", (short)0, UUID.randomUUID().toString()); when(networkService._accountMgr.getAccount(anyLong())).thenReturn(account); + UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN); + CallContext.register(user, account); + NetworkOfferingVO ntwkOff = new NetworkOfferingVO("offer", "fakeOffer", TrafficType.Guest, true, true, null, null, false, null, null, GuestType.Isolated, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false); @@ -139,21 +145,21 @@ public void createInvalidlyHostedPrivateNetwork() { /* Network nw; */ try { /* nw = */ - networkService.createPrivateNetwork("bla", "fake", 1L, "vlan:1", "10.1.1.2", null, "10.1.1.1", "255.255.255.0", 1L, 1L, true, 1L, false); + networkService.createPrivateNetwork("bla", "fake", 1L, "vlan:1", "10.1.1.2", null, "10.1.1.1", "255.255.255.0", 1L, 1L, true, 1L, false, null); /* nw = */ - networkService.createPrivateNetwork("bla", "fake", 1L, "lswitch:3", "10.1.1.2", null, "10.1.1.1", "255.255.255.0", 1L, 1L, false, 1L, false); + networkService.createPrivateNetwork("bla", "fake", 1L, "lswitch:3", "10.1.1.2", null, "10.1.1.1", "255.255.255.0", 1L, 1L, false, 1L, false, null); boolean invalid = false; boolean unsupported = false; try { /* nw = */ - networkService.createPrivateNetwork("bla", "fake", 1, "bla:2", "10.1.1.2", null, "10.1.1.1", "255.255.255.0", 1, 1L, true, 1L, false); + networkService.createPrivateNetwork("bla", "fake", 1, "bla:2", "10.1.1.2", null, "10.1.1.1", "255.255.255.0", 1, 1L, true, 1L, false, null); } catch (CloudRuntimeException e) { Assert.assertEquals("unexpected parameter exception", "string 'bla:2' has an unknown BroadcastDomainType.", e.getMessage()); invalid = true; } try { /* nw = */ - networkService.createPrivateNetwork("bla", "fake", 1, "mido://4", "10.1.1.2", null, "10.1.1.1", "255.255.255.0", 1, 1L, false, 1L, false); + networkService.createPrivateNetwork("bla", "fake", 1, "mido://4", "10.1.1.2", null, "10.1.1.1", "255.255.255.0", 1, 1L, false, 1L, false, null); } catch (InvalidParameterValueException e) { Assert.assertEquals("unexpected parameter exception", "unsupported type of broadcastUri specified: mido://4", e.getMessage()); unsupported = true; diff --git a/server/src/test/java/com/cloud/network/DedicateGuestVlanRangesTest.java b/server/src/test/java/com/cloud/network/DedicateGuestVlanRangesTest.java index 0531d0562a64..e517ee52ecd2 100644 --- a/server/src/test/java/com/cloud/network/DedicateGuestVlanRangesTest.java +++ b/server/src/test/java/com/cloud/network/DedicateGuestVlanRangesTest.java @@ -94,7 +94,7 @@ public void setup() throws Exception { networkService._accountDao = _accountDao; networkService._projectMgr = _projectMgr; networkService._physicalNetworkDao = _physicalNetworkDao; - networkService._datacneterVnet = _dataCenterVnetDao; + networkService._dcVnetDao = _dataCenterVnetDao; networkService._accountGuestVlanMapDao = _accountGuestVlanMapDao; Account account = new AccountVO("testaccount", 1, "networkdomain", (short)0, UUID.randomUUID().toString()); @@ -195,21 +195,21 @@ void runDedicateGuestVlanRangePostiveTest() throws Exception { when(networkService._physicalNetworkDao.findById(anyLong())).thenReturn(physicalNetwork); - when(networkService._datacneterVnet.listAllocatedVnetsInRange(anyLong(), anyLong(), anyInt(), anyInt())).thenReturn(null); + when(networkService._dcVnetDao.listAllocatedVnetsInRange(anyLong(), anyLong(), anyInt(), anyInt())).thenReturn(null); when(networkService._accountGuestVlanMapDao.listAccountGuestVlanMapsByPhysicalNetwork(anyLong())).thenReturn(null); when(networkService._accountGuestVlanMapDao.persist(any(AccountGuestVlanMapVO.class))).thenReturn(accountGuestVlanMapVO); - when(networkService._datacneterVnet.update(anyLong(), any(DataCenterVnetVO.class))).thenReturn(true); + when(networkService._dcVnetDao.update(anyLong(), any(DataCenterVnetVO.class))).thenReturn(true); List dataCenterVnetList = new ArrayList(); DataCenterVnetVO dataCenterVnetVO = new DataCenterVnetVO("2-5", 1L, 1L); dataCenterVnetList.add(dataCenterVnetVO); - when(networkService._datacneterVnet.findVnet(anyLong(), anyString())).thenReturn(dataCenterVnetList); + when(networkService._dcVnetDao.findVnet(anyLong(), anyString())).thenReturn(dataCenterVnetList); try { - GuestVlan result = networkService.dedicateGuestVlanRange(dedicateGuestVlanRangesCmd); + GuestVlanRange result = networkService.dedicateGuestVlanRange(dedicateGuestVlanRangesCmd); Assert.assertNotNull(result); } catch (Exception e) { s_logger.info("exception in testing runDedicateGuestVlanRangePostiveTest message: " + e.toString()); @@ -275,7 +275,7 @@ void runDedicateGuestVlanRangeAllocatedVlans() throws Exception { DataCenterVnetVO dataCenter = new DataCenterVnetVO("2-5", 1L, 1L); dataCenter.setAccountId(1L); dataCenterList.add(dataCenter); - when(networkService._datacneterVnet.listAllocatedVnetsInRange(anyLong(), anyLong(), anyInt(), anyInt())).thenReturn(dataCenterList); + when(networkService._dcVnetDao.listAllocatedVnetsInRange(anyLong(), anyLong(), anyInt(), anyInt())).thenReturn(dataCenterList); try { networkService.dedicateGuestVlanRange(dedicateGuestVlanRangesCmd); @@ -298,7 +298,7 @@ void runDedicateGuestVlanRangeDedicatedRange() throws Exception { when(networkService._physicalNetworkDao.findById(anyLong())).thenReturn(physicalNetwork); - when(networkService._datacneterVnet.listAllocatedVnetsInRange(anyLong(), anyLong(), anyInt(), anyInt())).thenReturn(null); + when(networkService._dcVnetDao.listAllocatedVnetsInRange(anyLong(), anyLong(), anyInt(), anyInt())).thenReturn(null); List guestVlanMaps = new ArrayList(); AccountGuestVlanMapVO accountGuestVlanMap = new AccountGuestVlanMapVO(1L, 1L); @@ -327,7 +327,7 @@ void runDedicateGuestVlanRangePartiallyDedicated() throws Exception { when(networkService._physicalNetworkDao.findById(anyLong())).thenReturn(physicalNetwork); - when(networkService._datacneterVnet.listAllocatedVnetsInRange(anyLong(), anyLong(), anyInt(), anyInt())).thenReturn(null); + when(networkService._dcVnetDao.listAllocatedVnetsInRange(anyLong(), anyLong(), anyInt(), anyInt())).thenReturn(null); List guestVlanMaps = new ArrayList(); AccountGuestVlanMapVO accountGuestVlanMap = new AccountGuestVlanMapVO(2L, 1L); @@ -349,7 +349,7 @@ void runReleaseDedicatedGuestVlanRangePostiveTest() throws Exception { AccountGuestVlanMapVO accountGuestVlanMap = new AccountGuestVlanMapVO(1L, 1L); when(networkService._accountGuestVlanMapDao.findById(anyLong())).thenReturn(accountGuestVlanMap); - doNothing().when(networkService._datacneterVnet).releaseDedicatedGuestVlans(anyLong()); + doNothing().when(networkService._dcVnetDao).releaseDedicatedGuestVlans(anyLong()); when(networkService._accountGuestVlanMapDao.remove(anyLong())).thenReturn(true); try { diff --git a/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java b/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java index 6fd57fc04d87..148ff1340ed7 100644 --- a/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java +++ b/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java @@ -524,6 +524,11 @@ public void checkNetworkPermissions(Account owner, Network network) { } + @Override + public void checkNetworkOperatePermissions(Account owner, Network network) { + + } + /* (non-Javadoc) * @see com.cloud.network.NetworkModel#getDefaultManagementTrafficLabel(long, com.cloud.hypervisor.Hypervisor.HypervisorType) */ diff --git a/server/src/test/java/com/cloud/network/UpdatePhysicalNetworkTest.java b/server/src/test/java/com/cloud/network/UpdatePhysicalNetworkTest.java index 022181e22172..4a7c2379a988 100644 --- a/server/src/test/java/com/cloud/network/UpdatePhysicalNetworkTest.java +++ b/server/src/test/java/com/cloud/network/UpdatePhysicalNetworkTest.java @@ -49,7 +49,7 @@ public NetworkServiceImpl setUp() { NetworkServiceImpl networkService = new NetworkServiceImpl(); networkService._dcDao = _datacenterDao; networkService._physicalNetworkDao = _physicalNetworkDao; - networkService._datacneterVnet = _datacenterVnetDao; + networkService._dcVnetDao = _datacenterVnetDao; return networkService; } diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java index 728effa8b764..2a4d2089ffef 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java @@ -28,6 +28,7 @@ import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin; import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd; import org.apache.cloudstack.api.command.admin.network.ListDedicatedGuestVlanRangesCmd; +import org.apache.cloudstack.api.command.admin.network.ListGuestVlansCmd; import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; @@ -49,6 +50,7 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.GuestVlan; +import com.cloud.network.GuestVlanRange; import com.cloud.network.IpAddress; import com.cloud.network.Network; import com.cloud.network.Network.IpAddresses; @@ -308,13 +310,13 @@ public boolean deletePhysicalNetwork(Long id) { } @Override - public GuestVlan dedicateGuestVlanRange(DedicateGuestVlanRangeCmd cmd) { + public GuestVlanRange dedicateGuestVlanRange(DedicateGuestVlanRangeCmd cmd) { // TODO Auto-generated method stub return null; } @Override - public Pair, Integer> listDedicatedGuestVlanRanges(ListDedicatedGuestVlanRangesCmd cmd) { + public Pair, Integer> listDedicatedGuestVlanRanges(ListDedicatedGuestVlanRangesCmd cmd) { // TODO Auto-generated method stub return null; } @@ -506,7 +508,7 @@ public IpAddress associateIPToNetwork(long ipId, long networkId) throws Insuffic */ @Override public Network createPrivateNetwork(String networkName, String displayText, long physicalNetworkId, String vlan, String startIp, String endIP, String gateway, - String netmask, long networkOwnerId, Long vpcId, Boolean sourceNat, Long networkOfferingId, Boolean bypassVlanOverlapCheck) throws ResourceAllocationException, ConcurrentOperationException, + String netmask, long networkOwnerId, Long vpcId, Boolean sourceNat, Long networkOfferingId, Boolean bypassVlanOverlapCheck, Long associatedNetworkId) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { // TODO Auto-generated method stub return null; @@ -963,6 +965,11 @@ public void rollbackNicForMigration(VirtualMachineProfile src, VirtualMachinePro } + @Override + public boolean isSharedNetworkWithoutSpecifyVlan(NetworkOffering offering) { + return false; + } + @Override public IpAddress updateIP(Long id, String customId, Boolean displayIp) { // TODO Auto-generated method stub @@ -997,4 +1004,9 @@ public Pair importNic(String macAddress, int deviceId, Netw @Override public void unmanageNics(VirtualMachineProfile vm) { } + + @Override + public Pair, Integer> listGuestVlans(ListGuestVlansCmd cmd) { + 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 a731f7c7c101..9544533efb0c 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java @@ -541,6 +541,11 @@ public void checkNetworkPermissions(Account owner, Network network) { } + @Override + public void checkNetworkOperatePermissions(Account owner, Network network) { + + } + /* (non-Javadoc) * @see com.cloud.network.NetworkModel#getDefaultManagementTrafficLabel(long, com.cloud.hypervisor.Hypervisor.HypervisorType) */ 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 c05b732042eb..caf2b910a25f 100644 --- a/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java +++ b/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java @@ -136,12 +136,12 @@ public void createSharedNtwkOffWithVlan() { assertNotNull("Shared network offering with specifyVlan=true failed to create ", off); } - @Test(expected=InvalidParameterValueException.class) + @Test 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, null, null, false); - assertNull("Shared network offering with specifyVlan=false was created", off); + assertNotNull("Shared network offering with specifyVlan=false was created", off); } @Test diff --git a/server/src/test/java/org/apache/cloudstack/privategw/AclOnPrivateGwTest.java b/server/src/test/java/org/apache/cloudstack/privategw/AclOnPrivateGwTest.java index 6a7dbd231c72..6f4ee69f4dd4 100644 --- a/server/src/test/java/org/apache/cloudstack/privategw/AclOnPrivateGwTest.java +++ b/server/src/test/java/org/apache/cloudstack/privategw/AclOnPrivateGwTest.java @@ -39,7 +39,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.command.admin.vpc.CreatePrivateGatewayCmd; +import org.apache.cloudstack.api.command.user.vpc.CreatePrivateGatewayCmd; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.test.utils.SpringUtils; diff --git a/test/integration/smoke/test_pvlan.py b/test/integration/smoke/test_pvlan.py index d7d5d3efb035..45b001a27185 100644 --- a/test/integration/smoke/test_pvlan.py +++ b/test/integration/smoke/test_pvlan.py @@ -30,12 +30,27 @@ class TestPVLAN(cloudstackTestCase): zoneId = 1 - networkOfferingId = 7 vlan = 2468 isolatedpvlan = 864 def setUp(self): self.apiClient = self.testClient.getApiClient() + list_shared_network_offerings = NetworkOffering.list( + self.apiClient, + name="DefaultSharedNetworkOffering", + displayText="Offering for Shared networks" + ) + self.assertEqual( + isinstance(list_shared_network_offerings, list), + True, + "List network offerings response was not a valid list" + ) + self.assertNotEqual( + len(list_shared_network_offerings), + 0, + "List network offerings response was empty" + ) + self.networkOfferingId = list_shared_network_offerings[0].id @attr(tags = ["advanced"], required_hardware="false") def test_create_pvlan_network(self): diff --git a/test/integration/smoke/test_storage_policy.py b/test/integration/smoke/test_storage_policy.py index ea35b4db69b3..dac610d70c6d 100644 --- a/test/integration/smoke/test_storage_policy.py +++ b/test/integration/smoke/test_storage_policy.py @@ -192,6 +192,8 @@ def test_01_import_storage_policies(self): self.apiclient, self.testdata["l2-network"], zoneid=self.zone.id, + accountid=self.account.name, + domainid=self.account.domainid, networkofferingid=self.network_offering.id ) self.cleanup.append(l2_network) diff --git a/test/integration/smoke/test_user_private_gateway.py b/test/integration/smoke/test_user_private_gateway.py new file mode 100644 index 000000000000..032d18ccf494 --- /dev/null +++ b/test/integration/smoke/test_user_private_gateway.py @@ -0,0 +1,425 @@ +# 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. + +""" +Tests of user-private gateway +""" + +import logging +import time + +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.lib.utils import cleanup_resources, random_gen + +from marvin.lib.base import (Account, + Domain, + Project, + Configurations, + ServiceOffering, + Zone, + Network, + NetworkOffering, + VPC, + VpcOffering, + PrivateGateway) + +from marvin.lib.common import (get_domain, + get_zone, + get_template) + +NETWORK_STATE_ALLOCATED = "Allocated" +NETWORK_STATE_IMPLEMENTED = "Implemented" +NETWORK_STATE_SETUP = "Setup" +NETWORK_STATE_REMOVED = "Removed" + +class TestUserPrivateGateways(cloudstackTestCase): + """ + Test user-shared networks + """ + @classmethod + def setUpClass(cls): + cls.testClient = super( + TestUserPrivateGateways, + cls).getClsTestClient() + cls.apiclient = cls.testClient.getApiClient() + cls.apiclient = cls.testClient.getApiClient() + cls.services = cls.testClient.getParsedTestDataConfig() + + zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests()) + cls.zone = Zone(zone.__dict__) + cls.template = get_template(cls.apiclient, cls.zone.id) + cls._cleanup = [] + + cls.logger = logging.getLogger("TestUserPrivateGateways") + cls.stream_handler = logging.StreamHandler() + cls.logger.setLevel(logging.DEBUG) + cls.logger.addHandler(cls.stream_handler) + + cls.domain = get_domain(cls.apiclient) + + # Create sub-domain + cls.sub_domain = Domain.create( + cls.apiclient, + cls.services["acl"]["domain1"] + ) + + # Create domain admin and normal user + cls.domain_admin = Account.create( + cls.apiclient, + cls.services["acl"]["accountD1A"], + admin=True, + domainid=cls.sub_domain.id + ) + cls.normal_user = Account.create( + cls.apiclient, + cls.services["acl"]["accountD1B"], + domainid=cls.sub_domain.id + ) + # Create project + cls.project = Project.create( + cls.apiclient, + cls.services["project"], + account=cls.domain_admin.name, + domainid=cls.domain_admin.domainid + ) + + # Create small service offering + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["small"] + ) + + # Create network offering for isolated networks + cls.network_offering_isolated = NetworkOffering.create( + cls.apiclient, + cls.services["network_offering"] + ) + cls.network_offering_isolated.update(cls.apiclient, state='Enabled') + + # Create vpc offering + cls.vpc_offering = VpcOffering.create( + cls.apiclient, + cls.services["vpc_offering_multi_lb"]) + cls.vpc_offering.update(cls.apiclient, state='Enabled') + + # Create network offering for vpc tiers + cls.network_offering_vpc = NetworkOffering.create( + cls.apiclient, + cls.services["nw_offering_isolated_vpc"], + conservemode=False + ) + cls.network_offering_vpc.update(cls.apiclient, state='Enabled') + + # Create api clients for domain admin and normal user + cls.domainadmin_user = cls.domain_admin.user[0] + cls.domainapiclient = cls.testClient.getUserApiClient( + cls.domainadmin_user.username, cls.sub_domain.name + ) + cls.normaluser_user = cls.normal_user.user[0] + cls.normaluser_apiclient = cls.testClient.getUserApiClient( + cls.normaluser_user.username, cls.sub_domain.name + ) + + cls._cleanup.append(cls.service_offering) + cls._cleanup.append(cls.network_offering_isolated) + cls._cleanup.append(cls.vpc_offering) + cls._cleanup.append(cls.network_offering_vpc) + + cls._cleanup.append(cls.sub_domain) + cls._cleanup.append(cls.normal_user) + cls._cleanup.append(cls.domain_admin) + cls._cleanup.append(cls.project) + + @classmethod + def tearDownClass(cls): + super(TestUserPrivateGateways, cls).tearDownClass() + + def setUp(self): + self.cleanup = [] + + def tearDown(self): + super(TestUserPrivateGateways, self).tearDown() + + def delete_network(self, network, apiclient, expected=True): + result = True + try: + Network.delete( + network, + apiclient, + ) + except Exception as ex: + result = False + if expected: + self.fail(f"Failed to remove Shared network, but expected to succeed : {ex}") + if result and not expected: + self.fail("network is removed successfully, but expected to fail") + + def create_isolated_network_for_account(self, apiclient, domain, account, project, expected=True): + self.services["network"]["name"] = "Test Network Isolated - " + random_gen() + domain_id = None + account_name = None + project_id = None + if domain: + domain_id = domain.id + if account: + account_name = account.name + if project: + project_id = project.id + try: + network = Network.create( + apiclient, + self.services["network"], + domainid=domain_id, + accountid=account_name, + projectid=project_id, + networkofferingid=self.network_offering_isolated.id, + zoneid=self.zone.id + ) + except Exception as ex: + network = None + if expected: + self.fail(f"Failed to create Isolated network, but expected to succeed : {ex}") + if network and not expected: + self.fail("Isolated network is created successfully, but expected to fail") + return network + + def check_network_state(self, apiclient, network, project, expected_state): + project_id = None + if project: + project_id = project.id + networks = Network.list( + apiclient, + listall=True, + projectid=project_id, + id=network.id + ) + if isinstance(networks, list) and len(networks) > 0: + if expected_state == NETWORK_STATE_REMOVED: + self.fail("Found the network, but expected to fail") + if networks[0].state != expected_state: + self.fail(f"Expect network state is {expected_state}, but actual state is {networks[0].state}") + elif expected_state != NETWORK_STATE_REMOVED: + self.fail("Failed to find the network, but expected to succeed") + + def create_vpc_for_account(self, apiclient, domain, account, project): + self.services["vpc"]["name"] = "Test VPC - " + random_gen() + self.services["vpc"]["cidr"] = "10.1.0.0/20" + domain_id = None + account_name = None + if domain: + domain_id = domain.id + if account: + account_name = account.name + project_id = None + if project: + project_id = project.id + vpc = VPC.create( + apiclient, + self.services["vpc"], + domainid=domain_id, + accountid=account_name, + projectid=project_id, + vpcofferingid=self.vpc_offering.id, + zoneid=self.zone.id, + start=False + ) + return vpc + + def create_vpc_tier_for_account(self, apiclient, vpc, project=None, gateway = '10.1.1.1'): + self.services["network"]["name"] = "Test VPC tier - " + random_gen() + project_id = None + if project: + project_id = project.id + vpc_tier = Network.create( + apiclient, + self.services["network"], + networkofferingid=self.network_offering_vpc.id, + zoneid=self.zone.id, + projectid=project_id, + gateway=gateway, + netmask="255.255.255.0", + vpcid=vpc.id + ) + return vpc_tier + + def create_vpc_private_gateway(self, apiclient, vpc, vlan_id, associated_network=None, expected=True): + self.services["private_gateway"]["name"] = "Test Network Isolated - " + random_gen() + associated_network_id = None + if associated_network: + associated_network_id = associated_network.id + private_gateway = None + try: + private_gateway = PrivateGateway.create( + apiclient, + vpcid=vpc.id, + gateway = self.services["private_gateway"]["gateway"], + ipaddress = self.services["private_gateway"]["ipaddress"], + netmask = self.services["private_gateway"]["netmask"], + vlan = vlan_id, + associatednetworkid = associated_network_id + ) + except Exception as ex: + private_gateway = None + if expected: + self.fail(f"Failed to create private gateway, but expected to succeed : {ex}") + if private_gateway and not expected: + self.fail("private gateway is created successfully, but expected to fail") + return private_gateway + + def delete_vpc_private_gateway(self, apiclient, private_gateway, expected=True): + result = True + try: + private_gateway.delete(apiclient) + except Exception as ex: + result = False + if expected: + self.fail(f"Failed to remove private gateway, but expected to succeed : {ex}") + if result and not expected: + self.fail("private gateway is removed successfully, but expected to fail") + + def delete_vpc(self, apiclient, vpc, expected=True): + result = True + try: + vpc.delete(apiclient) + except Exception as ex: + result = False + if expected: + self.fail(f"Failed to remove VPC, but expected to succeed : {ex}") + if result and not expected: + self.fail("VPC is removed successfully, but expected to fail") + + @attr(tags=["advanced"], required_hardware="false") + def test_01_create_private_gateway_with_vlan(self): + """ Create private gateway with vlan """ + + # Create VPC + vpc1 = self.create_vpc_for_account(self.apiclient, None, None, None) + vpc2 = self.create_vpc_for_account(self.domainapiclient, None, None, None) + vpc3 = self.create_vpc_for_account(self.normaluser_apiclient, None, None, None) + vpc4 = self.create_vpc_for_account(self.domainapiclient, None, None, self.project) + + # Create VPC private gateway with vlan (can only be done by ROOT admin) + private_gateway1 = self.create_vpc_private_gateway(self.apiclient, vpc1, 10, None, True) + self.create_vpc_private_gateway(self.domainapiclient, vpc2, 11, None, False) + self.create_vpc_private_gateway(self.normaluser_apiclient, vpc3, 12, None, False) + self.create_vpc_private_gateway(self.domainapiclient, vpc4, 13, None, False) + private_gateway2 = self.create_vpc_private_gateway(self.apiclient, vpc2, 11, None, True) + private_gateway3 = self.create_vpc_private_gateway(self.apiclient, vpc3, 12, None, True) + private_gateway4 = self.create_vpc_private_gateway(self.apiclient, vpc4, 13, None, True) + + # Delete VPC private gateway (should succeed by ROOT admin) + self.delete_vpc_private_gateway(self.apiclient, private_gateway1, True) + self.delete_vpc_private_gateway(self.domainapiclient, private_gateway2, False) + self.delete_vpc_private_gateway(self.normaluser_apiclient, private_gateway2, False) + self.delete_vpc_private_gateway(self.domainapiclient, private_gateway4, False) + self.delete_vpc_private_gateway(self.apiclient, private_gateway2, True) + self.delete_vpc_private_gateway(self.apiclient, private_gateway3, True) + self.delete_vpc_private_gateway(self.apiclient, private_gateway4, True) + + # Delete VPC + self.delete_vpc(self.apiclient, vpc1) + self.delete_vpc(self.domainapiclient, vpc2) + self.delete_vpc(self.normaluser_apiclient, vpc3) + self.delete_vpc(self.domainapiclient, vpc4) + + @attr(tags=["advanced"], required_hardware="false") + def test_02_create_private_gateway_with_associated_network(self): + """ Create private gateway with associated network """ + + self.services["network"]["networkoffering"] = self.network_offering_isolated.id + self.services["network"]["vlan"] = None + + # Create isolated networks + isolated_network1 = self.create_isolated_network_for_account(self.apiclient, None, None, None) + isolated_network2 = self.create_isolated_network_for_account(self.apiclient, self.sub_domain, self.domain_admin, None) + isolated_network3 = self.create_isolated_network_for_account(self.apiclient, self.sub_domain, self.normal_user, None) + isolated_network4 = self.create_isolated_network_for_account(self.apiclient, self.sub_domain, None, self.project) + + # Check state of isolated networks (should be Allocated) + self.check_network_state(self.apiclient, isolated_network1, None, NETWORK_STATE_ALLOCATED) + self.check_network_state(self.domainapiclient, isolated_network2, None, NETWORK_STATE_ALLOCATED) + self.check_network_state(self.normaluser_apiclient, isolated_network3, None, NETWORK_STATE_ALLOCATED) + self.check_network_state(self.domainapiclient, isolated_network4, self.project, NETWORK_STATE_ALLOCATED) + + # Create VPC + vpc1 = self.create_vpc_for_account(self.apiclient, None, None, None) + vpc2 = self.create_vpc_for_account(self.domainapiclient, None, None, None) + vpc3 = self.create_vpc_for_account(self.normaluser_apiclient, None, None, None) + vpc4 = self.create_vpc_for_account(self.domainapiclient, None, None, self.project) + + # Create VPC tier + vpc1_tier1 = self.create_vpc_tier_for_account(self.apiclient, vpc1) + vpc2_tier1 = self.create_vpc_tier_for_account(self.domainapiclient, vpc2) + vpc3_tier1 = self.create_vpc_tier_for_account(self.normaluser_apiclient, vpc3) + vpc4_tier1 = self.create_vpc_tier_for_account(self.domainapiclient, vpc4, self.project) + + # Create VPC private gateway with associated network (can be done by ROOT admin / domain admin / normal user) + private_gateway1 = self.create_vpc_private_gateway(self.apiclient, vpc1, None, isolated_network1, True) + private_gateway2 = self.create_vpc_private_gateway(self.domainapiclient, vpc2, None, isolated_network2, True) + private_gateway3 = self.create_vpc_private_gateway(self.normaluser_apiclient, vpc3, None, isolated_network3, True) + private_gateway4 = self.create_vpc_private_gateway(self.domainapiclient, vpc4, None, isolated_network4, True) + + # Check state of isolated networks (should be Implemented) + self.check_network_state(self.apiclient, isolated_network1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, isolated_network2, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.normaluser_apiclient, isolated_network3, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, isolated_network4, self.project, NETWORK_STATE_IMPLEMENTED) + + # Delete VPC private gateway of domain admin (should succeed) + self.delete_vpc_private_gateway(self.domainapiclient, private_gateway2, True) + + # Wait for network GC to shut down the isolated networks + gc_wait = Configurations.list(self.apiclient, name="network.gc.wait") + gc_interval = Configurations.list(self.apiclient, name="network.gc.interval") + total_sleep = 360 + if gc_wait and gc_interval: + self.logger.debug("network.gc.wait is ==> %s", gc_wait[0].value) + self.logger.debug("network.gc.interval is ==> %s", gc_interval[0].value) + total_sleep = max(int(gc_wait[0].value), int(gc_interval[0].value)) * 2 + 60 + else: + self.logger.debug("Could not retrieve the keys 'network.gc.interval' and 'network.gc.wait'. Sleeping for 6 minutes.") + time.sleep(total_sleep) + + # Check state of isolated networks (1 should be Allocated, 2 should still be Implemented) + self.check_network_state(self.apiclient, isolated_network1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, isolated_network2, None, NETWORK_STATE_ALLOCATED) + self.check_network_state(self.normaluser_apiclient, isolated_network3, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, isolated_network4, self.project, NETWORK_STATE_IMPLEMENTED) + + # Delete 2 VPC private gateway (should succeed) + self.delete_vpc_private_gateway(self.apiclient, private_gateway1, True) + self.delete_vpc_private_gateway(self.apiclient, private_gateway3, True) + self.delete_vpc_private_gateway(self.apiclient, private_gateway4, True) + + # Delete VPC tiers (should succeed) + self.delete_network(vpc1_tier1, self.apiclient, True) + self.delete_network(vpc2_tier1, self.domainapiclient, True) + self.delete_network(vpc3_tier1, self.normaluser_apiclient, True) + self.delete_network(vpc4_tier1, self.domainapiclient, True) + + # Delete VPC + self.delete_vpc(self.apiclient, vpc1) + self.delete_vpc(self.domainapiclient, vpc2) + self.delete_vpc(self.normaluser_apiclient, vpc3) + self.delete_vpc(self.domainapiclient, vpc4) + + # Delete isolated networks (should succeed) + self.delete_network(isolated_network1, self.apiclient, True) + self.delete_network(isolated_network2, self.domainapiclient, True) + self.delete_network(isolated_network3, self.normaluser_apiclient, True) + self.delete_network(isolated_network4, self.domainapiclient, True) diff --git a/test/integration/smoke/test_user_shared_network.py b/test/integration/smoke/test_user_shared_network.py new file mode 100644 index 000000000000..fefadc51561e --- /dev/null +++ b/test/integration/smoke/test_user_shared_network.py @@ -0,0 +1,630 @@ +# 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. + +""" +Tests of user-shared networks +""" + +import logging +import time + +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.lib.utils import cleanup_resources, random_gen + +from marvin.lib.base import (Account, + Domain, + Project, + Configurations, + ServiceOffering, + Zone, + Network, + NetworkOffering, + VPC, + VpcOffering) + +from marvin.lib.common import (get_domain, + get_zone, + get_template) + +NETWORK_STATE_ALLOCATED = "Allocated" +NETWORK_STATE_IMPLEMENTED = "Implemented" +NETWORK_STATE_SETUP = "Setup" +NETWORK_STATE_REMOVED = "Removed" + +class TestUserSharedNetworks(cloudstackTestCase): + """ + Test user-shared networks + """ + @classmethod + def setUpClass(cls): + cls.testClient = super( + TestUserSharedNetworks, + cls).getClsTestClient() + cls.apiclient = cls.testClient.getApiClient() + cls.services = cls.testClient.getParsedTestDataConfig() + + zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests()) + cls.zone = Zone(zone.__dict__) + cls.template = get_template(cls.apiclient, cls.zone.id) + cls._cleanup = [] + + cls.logger = logging.getLogger("TestUserSharedNetworks") + cls.stream_handler = logging.StreamHandler() + cls.logger.setLevel(logging.DEBUG) + cls.logger.addHandler(cls.stream_handler) + + cls.domain = get_domain(cls.apiclient) + + # Create sub-domain + cls.sub_domain = Domain.create( + cls.apiclient, + cls.services["acl"]["domain1"] + ) + + # Create domain admin and normal user + cls.domain_admin = Account.create( + cls.apiclient, + cls.services["acl"]["accountD1A"], + admin=True, + domainid=cls.sub_domain.id + ) + cls.normal_user = Account.create( + cls.apiclient, + cls.services["acl"]["accountD1B"], + domainid=cls.sub_domain.id + ) + # Create project + cls.project = Project.create( + cls.apiclient, + cls.services["project"], + account=cls.domain_admin.name, + domainid=cls.domain_admin.domainid + ) + + # Create small service offering + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["small"] + ) + + # Create network offering for user-shared networks (specifyVlan=true) + cls.network_offering_withvlan = NetworkOffering.create( + cls.apiclient, + cls.services["network_offering_shared"] + ) + cls.network_offering_withvlan.update(cls.apiclient, state='Enabled') + + # Create network offering for user-shared networks (specifyVlan=false) + cls.services["network_offering_shared"]["specifyVlan"] = "False" + cls.network_offering_novlan = NetworkOffering.create( + cls.apiclient, + cls.services["network_offering_shared"] + ) + cls.network_offering_novlan.update(cls.apiclient, state='Enabled') + + # Create network offering for isolated networks + cls.network_offering_isolated = NetworkOffering.create( + cls.apiclient, + cls.services["network_offering"] + ) + cls.network_offering_isolated.update(cls.apiclient, state='Enabled') + + # Create vpc offering + cls.vpc_offering = VpcOffering.create( + cls.apiclient, + cls.services["vpc_offering_multi_lb"]) + cls.vpc_offering.update(cls.apiclient, state='Enabled') + + # Create network offering for vpc tiers + cls.network_offering_vpc = NetworkOffering.create( + cls.apiclient, + cls.services["nw_offering_isolated_vpc"], + conservemode=False + ) + cls.network_offering_vpc.update(cls.apiclient, state='Enabled') + + # Create api clients for domain admin and normal user + cls.domainadmin_user = cls.domain_admin.user[0] + cls.domainapiclient = cls.testClient.getUserApiClient( + cls.domainadmin_user.username, cls.sub_domain.name + ) + cls.normaluser_user = cls.normal_user.user[0] + cls.normaluser_apiclient = cls.testClient.getUserApiClient( + cls.normaluser_user.username, cls.sub_domain.name + ) + + cls._cleanup.append(cls.service_offering) + cls._cleanup.append(cls.network_offering_withvlan) + cls._cleanup.append(cls.network_offering_novlan) + cls._cleanup.append(cls.network_offering_isolated) + cls._cleanup.append(cls.network_offering_vpc) + cls._cleanup.append(cls.vpc_offering) + + cls._cleanup.append(cls.sub_domain) + cls._cleanup.append(cls.normal_user) + cls._cleanup.append(cls.domain_admin) + cls._cleanup.append(cls.project) + + @classmethod + def tearDownClass(cls): + super(TestUserSharedNetworks, cls).tearDownClass() + + def setUp(self): + self.cleanup = [] + + def tearDown(self): + super(TestUserSharedNetworks, self).tearDown() + + def create_shared_network_for_account(self, apiclient, domain, account, expected=True): + return self.create_shared_network_with_associated_network(apiclient, domain, account, None, None, expected) + + def create_shared_network_with_associated_network_for_domain(self, apiclient, domain, associated_network, expected=True): + return self.create_shared_network_with_associated_network(apiclient, domain, None, None, associated_network, expected) + + def create_shared_network_with_associated_network_for_caller(self, apiclient, project, associated_network, expected=True): + return self.create_shared_network_with_associated_network(apiclient, None, None, project, associated_network, expected) + + def create_shared_network_with_associated_network(self, apiclient, domain, account, project, associated_network, expected=True): + self.services["network2"]["acltype"] = "Account" + self.services["network2"]["name"] = "Test Network Shared - " + random_gen() + domain_id = None + account_name = None + project_id = None + if domain: + self.services["network2"]["acltype"] = "Domain" + domain_id = domain.id + if account: + self.services["network2"]["acltype"] = "Account" + account_name = account.name + if project: + self.services["network2"]["acltype"] = "Account" + project_id = project.id + associated_network_id = None + if associated_network: + associated_network_id = associated_network.id + try: + network = Network.create( + apiclient, + self.services["network2"], + domainid=domain_id, + accountid=account_name, + projectid=project_id, + associatednetworkid=associated_network_id, + zoneid=self.zone.id + ) + except Exception as ex: + network = None + if expected: + self.fail(f"Failed to create Shared network, but expected to succeed : {ex}") + if network and not expected: + self.fail("Shared network is created successfully, but expected to fail") + return network + + def delete_network(self, network, apiclient, expected=True): + result = True + try: + Network.delete( + network, + apiclient, + ) + except Exception as ex: + result = False + if expected: + self.fail(f"Failed to remove Shared network, but expected to succeed : {ex}") + if result and not expected: + self.fail("network is removed successfully, but expected to fail") + + def create_isolated_network_for_account(self, apiclient, domain, account, project, expected=True): + self.services["network"]["name"] = "Test Network Isolated - " + random_gen() + domain_id = None + account_name = None + project_id = None + if domain: + domain_id = domain.id + if account: + account_name = account.name + if project: + project_id = project.id + try: + network = Network.create( + apiclient, + self.services["network"], + domainid=domain_id, + accountid=account_name, + projectid=project_id, + networkofferingid=self.network_offering_isolated.id, + zoneid=self.zone.id + ) + except Exception as ex: + network = None + if expected: + self.fail(f"Failed to create Isolated network, but expected to succeed : {ex}") + if network and not expected: + self.fail("Isolated network is created successfully, but expected to fail") + return network + + def check_network_state(self, apiclient, network, project, expected_state): + project_id = None + if project: + project_id = project.id + networks = Network.list( + apiclient, + listall=True, + projectid=project_id, + id=network.id + ) + if isinstance(networks, list) and len(networks) > 0: + if expected_state == NETWORK_STATE_REMOVED: + self.fail("Found the network, but expected to fail") + if networks[0].state != expected_state: + self.fail(f"Expect network state is {expected_state}, but actual state is {networks[0].state}") + elif expected_state != NETWORK_STATE_REMOVED: + self.fail("Failed to find the network, but expected to succeed") + + def create_vpc_for_account(self, apiclient, domain, account, project): + self.services["vpc"]["name"] = "Test VPC - " + random_gen() + self.services["vpc"]["displaytext"] = self.services["vpc"]["name"] + self.services["vpc"]["cidr"] = "10.1.0.0/20" + domain_id = None + account_name = None + if domain: + domain_id = domain.id + if account: + account_name = account.name + project_id = None + if project: + project_id = project.id + vpc = VPC.create( + apiclient, + self.services["vpc"], + domainid=domain_id, + accountid=account_name, + projectid=project_id, + vpcofferingid=self.vpc_offering.id, + zoneid=self.zone.id, + start=False + ) + return vpc + + def create_vpc_tier_for_account(self, apiclient, vpc, project=None, gateway = '10.1.1.1'): + self.services["network"]["name"] = "Test VPC tier - " + random_gen() + project_id = None + if project: + project_id = project.id + vpc_tier = Network.create( + apiclient, + self.services["network"], + networkofferingid=self.network_offering_vpc.id, + zoneid=self.zone.id, + projectid=project_id, + gateway=gateway, + netmask="255.255.255.0", + vpcid=vpc.id + ) + return vpc_tier + + def delete_vpc(self, apiclient, vpc, expected=True): + result = True + try: + vpc.delete(apiclient) + except Exception as ex: + result = False + if expected: + self.fail(f"Failed to remove VPC, but expected to succeed : {ex}") + if result and not expected: + self.fail("VPC is removed successfully, but expected to fail") + + @attr(tags=["advanced"], required_hardware="false") + def test_01_create_user_shared_network_without_vlan(self): + """ Create user-shared networks without vlan """ + self.services["network2"]["networkoffering"] = self.network_offering_novlan.id + self.services["network2"]["vlan"] = None + + network1 = self.create_shared_network_for_account(self.apiclient, None, None) + network2 = self.create_shared_network_for_account(self.domainapiclient, None, None) + network3 = self.create_shared_network_for_account(self.normaluser_apiclient, None, None) + + self.delete_network(network1, self.apiclient) + self.delete_network(network2, self.domainapiclient) + self.delete_network(network3, self.normaluser_apiclient) + + network4 = self.create_shared_network_for_account(self.domainapiclient, self.sub_domain, self.normal_user, True) + self.delete_network(network4, self.normaluser_apiclient) + + network5 = self.create_shared_network_for_account(self.apiclient, self.sub_domain, None, True) + self.delete_network(network5, self.apiclient) + network6 = self.create_shared_network_for_account(self.domainapiclient, self.sub_domain, None, True) + self.delete_network(network6, self.apiclient) + self.create_shared_network_for_account(self.normaluser_apiclient, self.sub_domain, None, False) + + @attr(tags=["advanced"], required_hardware="false") + def test_02_create_shared_network_with_vlan(self): + """ Create shared networks with vlan + Only be created/deleted by root admin, Cannot create networks with same vlan + """ + self.services["network2"]["networkoffering"] = self.network_offering_withvlan.id + self.services["network2"]["vlan"] = 4000 + + self.create_shared_network_for_account(self.domainapiclient, self.sub_domain, None, False) + self.create_shared_network_for_account(self.normaluser_apiclient, self.sub_domain, None, False) + network1 = self.create_shared_network_for_account(self.apiclient, self.sub_domain, None, True) + self.create_shared_network_for_account(self.apiclient, self.sub_domain, self.normal_user, False) + self.delete_network(network1, self.domainapiclient, False) + self.delete_network(network1, self.apiclient, True) + + self.create_shared_network_for_account(self.domainapiclient, self.sub_domain, self.normal_user, False) + self.create_shared_network_for_account(self.normaluser_apiclient, self.sub_domain, self.normal_user, False) + network2 = self.create_shared_network_for_account(self.apiclient, self.sub_domain, self.normal_user, True) + self.delete_network(network2, self.domainapiclient, False) + self.delete_network(network2, self.normaluser_apiclient, False) + self.delete_network(network2, self.apiclient, True) + + @attr(tags=["advanced"], required_hardware="false") + def test_03_create_domain_shared_network_with_associated_network(self): + """ Create domain-level shared networks with associated network """ + + self.services["network2"]["networkoffering"] = self.network_offering_novlan.id + self.services["network2"]["vlan"] = None + + # Create isolated networks + isolated_network1 = self.create_isolated_network_for_account(self.apiclient, None, None, None, True) + isolated_network2 = self.create_isolated_network_for_account(self.apiclient, self.sub_domain, self.domain_admin, None, True) + isolated_network3 = self.create_isolated_network_for_account(self.apiclient, self.sub_domain, self.normal_user, None, True) + isolated_network4 = self.create_isolated_network_for_account(self.apiclient, self.sub_domain, None, self.project, True) + + # Create domain-level shared network with associated_network (caller must be root admin/domain admin, must be in same domain) + self.create_shared_network_with_associated_network_for_domain(self.apiclient, self.sub_domain, isolated_network1, False) + self.create_shared_network_with_associated_network_for_domain(self.domainapiclient, self.sub_domain, isolated_network1, False) + self.create_shared_network_with_associated_network_for_domain(self.normaluser_apiclient, self.sub_domain, isolated_network1, False) + self.create_shared_network_with_associated_network_for_domain(self.normaluser_apiclient, self.sub_domain, isolated_network2, False) + self.create_shared_network_with_associated_network_for_domain(self.normaluser_apiclient, self.sub_domain, isolated_network3, False) + self.create_shared_network_with_associated_network_for_domain(self.normaluser_apiclient, self.sub_domain, isolated_network4, False) + shared_network1 = self.create_shared_network_with_associated_network_for_domain(self.apiclient, self.domain, isolated_network1, True) + shared_network2 = self.create_shared_network_with_associated_network_for_domain(self.domainapiclient, self.sub_domain, isolated_network2, True) + shared_network3 = self.create_shared_network_with_associated_network_for_domain(self.domainapiclient, self.sub_domain, isolated_network3, True) + shared_network4 = self.create_shared_network_with_associated_network_for_domain(self.domainapiclient, self.sub_domain, isolated_network4, True) + + # Check state of isolated networks (should be Implemented) + self.check_network_state(self.apiclient, isolated_network1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, isolated_network2, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.normaluser_apiclient, isolated_network3, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, isolated_network4, self.project, NETWORK_STATE_IMPLEMENTED) + + # Delete isolated networks (should fail) + self.delete_network(isolated_network1, self.apiclient, False) + self.delete_network(isolated_network2, self.apiclient, False) + self.delete_network(isolated_network3, self.apiclient, False) + self.delete_network(isolated_network4, self.apiclient, False) + + # Create VPC + vpc1 = self.create_vpc_for_account(self.apiclient, None, None, None) + vpc2 = self.create_vpc_for_account(self.domainapiclient, None, None, None) + vpc3 = self.create_vpc_for_account(self.normaluser_apiclient, None, None, None) + vpc4 = self.create_vpc_for_account(self.domainapiclient, None, None, self.project) + + # Create VPC tier + vpc1_tier1 = self.create_vpc_tier_for_account(self.apiclient, vpc1) + vpc2_tier1 = self.create_vpc_tier_for_account(self.domainapiclient, vpc2) + vpc3_tier1 = self.create_vpc_tier_for_account(self.normaluser_apiclient, vpc3) + vpc4_tier1 = self.create_vpc_tier_for_account(self.domainapiclient, vpc4, self.project) + + # Create domain-level shared network with associated vpc tier (caller must be root admin/domain admin, must be in same domain) + self.create_shared_network_with_associated_network_for_domain(self.apiclient, self.sub_domain, vpc1_tier1, False) + self.create_shared_network_with_associated_network_for_domain(self.domainapiclient, self.sub_domain, vpc1_tier1, False) + self.create_shared_network_with_associated_network_for_domain(self.normaluser_apiclient, self.sub_domain, vpc1_tier1, False) + self.create_shared_network_with_associated_network_for_domain(self.normaluser_apiclient, self.sub_domain, vpc2_tier1, False) + self.create_shared_network_with_associated_network_for_domain(self.normaluser_apiclient, self.sub_domain, vpc3_tier1, False) + self.create_shared_network_with_associated_network_for_domain(self.normaluser_apiclient, self.sub_domain, vpc4_tier1, False) + shared_network_vpctier1 = self.create_shared_network_with_associated_network_for_domain(self.apiclient, self.domain, vpc1_tier1, True) + shared_network_vpctier2 = self.create_shared_network_with_associated_network_for_domain(self.domainapiclient, self.sub_domain, vpc2_tier1, True) + shared_network_vpctier3 = self.create_shared_network_with_associated_network_for_domain(self.domainapiclient, self.sub_domain, vpc3_tier1, True) + shared_network_vpctier4 = self.create_shared_network_with_associated_network_for_domain(self.domainapiclient, self.sub_domain, vpc4_tier1, True) + + # Check state of vpc tiers (should be Implemented) + self.check_network_state(self.apiclient, vpc1_tier1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, vpc2_tier1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.normaluser_apiclient, vpc3_tier1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, vpc4_tier1, self.project, NETWORK_STATE_IMPLEMENTED) + + # Delete vpc tiers(should fail) + self.delete_network(vpc1_tier1, self.apiclient, False) + self.delete_network(vpc2_tier1, self.apiclient, False) + self.delete_network(vpc3_tier1, self.apiclient, False) + self.delete_network(vpc4_tier1, self.apiclient, False) + + # Delete shared networks associated to domain admin's isolated network or vpc tier(should succeed) + self.delete_network(shared_network2, self.domainapiclient, True) + self.delete_network(shared_network_vpctier2, self.domainapiclient, True) + + # Wait for network GC to shut down the isolated networks + gc_wait = Configurations.list(self.apiclient, name="network.gc.wait") + gc_interval = Configurations.list(self.apiclient, name="network.gc.interval") + total_sleep = 360 + if gc_wait and gc_interval: + self.logger.debug("network.gc.wait is ==> %s", gc_wait[0].value) + self.logger.debug("network.gc.interval is ==> %s", gc_interval[0].value) + total_sleep = max(int(gc_wait[0].value), int(gc_interval[0].value)) * 2 + 60 + else: + self.logger.debug("Could not retrieve the keys 'network.gc.interval' and 'network.gc.wait'. Sleeping for 6 minutes.") + time.sleep(total_sleep) + + # Check state of isolated networks (1 should be Allocated, 3 should still be Implemented) + self.check_network_state(self.apiclient, isolated_network1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, isolated_network2, None, NETWORK_STATE_ALLOCATED) + self.check_network_state(self.normaluser_apiclient, isolated_network3, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, isolated_network4, self.project, NETWORK_STATE_IMPLEMENTED) + + self.check_network_state(self.apiclient, vpc1_tier1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, vpc2_tier1, None, NETWORK_STATE_ALLOCATED) + self.check_network_state(self.normaluser_apiclient, vpc3_tier1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, vpc4_tier1, self.project, NETWORK_STATE_IMPLEMENTED) + + # Check state of shared network (should be Setup) + self.check_network_state(self.apiclient, shared_network1, None, NETWORK_STATE_SETUP) + self.check_network_state(self.normaluser_apiclient, shared_network3, None, NETWORK_STATE_SETUP) + self.check_network_state(self.domainapiclient, shared_network4, None, NETWORK_STATE_SETUP) + + self.check_network_state(self.apiclient, shared_network_vpctier1, None, NETWORK_STATE_SETUP) + self.check_network_state(self.normaluser_apiclient, shared_network_vpctier3, None, NETWORK_STATE_SETUP) + self.check_network_state(self.domainapiclient, shared_network_vpctier4, None, NETWORK_STATE_SETUP) + + # Delete admin's shared networks (should succeed) + self.delete_network(shared_network1, self.apiclient, True) + self.delete_network(shared_network3, self.domainapiclient, True) + self.delete_network(shared_network4, self.domainapiclient, True) + + self.delete_network(shared_network_vpctier1, self.apiclient, True) + self.delete_network(shared_network_vpctier3, self.domainapiclient, True) + self.delete_network(shared_network_vpctier4, self.domainapiclient, True) + + # Delete admin's and domain admin's isolated network, but keep the normal user's network + # normal user's shared network and isolated network should be removed in tearDown successfully + self.delete_network(isolated_network1, self.apiclient, True) + self.delete_network(isolated_network2, self.domainapiclient, True) + + self.delete_network(vpc1_tier1, self.apiclient, True) + self.delete_vpc(self.apiclient, vpc1) + + @attr(tags=["advanced"], required_hardware="false") + def test_04_create_account_shared_network_with_associated_network(self): + """ Create account-level shared networks with associated network """ + + self.services["network2"]["networkoffering"] = self.network_offering_novlan.id + self.services["network2"]["vlan"] = None + + # Create isolated networks + isolated_network1 = self.create_isolated_network_for_account(self.apiclient, None, None, None, True) + isolated_network2 = self.create_isolated_network_for_account(self.apiclient, self.sub_domain, self.domain_admin, None, True) + isolated_network3 = self.create_isolated_network_for_account(self.apiclient, self.sub_domain, self.normal_user, None, True) + isolated_network4 = self.create_isolated_network_for_account(self.apiclient, self.sub_domain, None, self.project, True) + + # Check state of isolated networks (should be Allocated) + self.check_network_state(self.apiclient, isolated_network1, None, NETWORK_STATE_ALLOCATED) + self.check_network_state(self.domainapiclient, isolated_network2, None, NETWORK_STATE_ALLOCATED) + self.check_network_state(self.normaluser_apiclient, isolated_network3, None, NETWORK_STATE_ALLOCATED) + self.check_network_state(self.domainapiclient, isolated_network4, self.project, NETWORK_STATE_ALLOCATED) + + # Create shared networks with associated network (must be same owner) + self.create_shared_network_with_associated_network_for_caller(self.domainapiclient, None, isolated_network1, False) + self.create_shared_network_with_associated_network_for_caller(self.domainapiclient, None, isolated_network3, False) + self.create_shared_network_with_associated_network_for_caller(self.domainapiclient, None, isolated_network4, False) + self.create_shared_network_with_associated_network_for_caller(self.normaluser_apiclient, None, isolated_network1, False) + self.create_shared_network_with_associated_network_for_caller(self.normaluser_apiclient, None, isolated_network2, False) + self.create_shared_network_with_associated_network_for_caller(self.normaluser_apiclient, None, isolated_network4, False) + shared_network1 = self.create_shared_network_with_associated_network_for_caller(self.apiclient, None, isolated_network1, True) + shared_network2 = self.create_shared_network_with_associated_network_for_caller(self.domainapiclient, None, isolated_network2, True) + shared_network3 = self.create_shared_network_with_associated_network_for_caller(self.normaluser_apiclient, None, isolated_network3, True) + shared_network4 = self.create_shared_network_with_associated_network_for_caller(self.domainapiclient, self.project, isolated_network4, True) + + # Check state of isolated networks (should be Implemented) + self.check_network_state(self.apiclient, isolated_network1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, isolated_network2, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.normaluser_apiclient, isolated_network3, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, isolated_network4, self.project, NETWORK_STATE_IMPLEMENTED) + + # Delete isolated networks (should fail) + self.delete_network(isolated_network1, self.apiclient, False) + self.delete_network(isolated_network2, self.apiclient, False) + self.delete_network(isolated_network3, self.apiclient, False) + self.delete_network(isolated_network4, self.apiclient, False) + + # Create VPC + vpc1 = self.create_vpc_for_account(self.apiclient, None, None, None) + vpc2 = self.create_vpc_for_account(self.domainapiclient, None, None, None) + vpc3 = self.create_vpc_for_account(self.normaluser_apiclient, None, None, None) + vpc4 = self.create_vpc_for_account(self.domainapiclient, None, None, self.project) + + # Create VPC tier + vpc1_tier1 = self.create_vpc_tier_for_account(self.apiclient, vpc1) + vpc2_tier1 = self.create_vpc_tier_for_account(self.domainapiclient, vpc2) + vpc3_tier1 = self.create_vpc_tier_for_account(self.normaluser_apiclient, vpc3) + vpc4_tier1 = self.create_vpc_tier_for_account(self.domainapiclient, vpc4, self.project) + + # Create account-level shared network with associated vpc tier (caller must be root admin/domain admin, must be in same domain) + self.create_shared_network_with_associated_network_for_caller(self.domainapiclient, None, vpc1_tier1, False) + self.create_shared_network_with_associated_network_for_caller(self.domainapiclient, None, vpc3_tier1, False) + self.create_shared_network_with_associated_network_for_caller(self.domainapiclient, None, vpc4_tier1, False) + self.create_shared_network_with_associated_network_for_caller(self.normaluser_apiclient, None, vpc1_tier1, False) + self.create_shared_network_with_associated_network_for_caller(self.normaluser_apiclient, None, vpc2_tier1, False) + self.create_shared_network_with_associated_network_for_caller(self.normaluser_apiclient, None, vpc4_tier1, False) + shared_network_vpctier1 = self.create_shared_network_with_associated_network_for_caller(self.apiclient, None, vpc1_tier1, True) + shared_network_vpctier2 = self.create_shared_network_with_associated_network_for_caller(self.domainapiclient, None, vpc2_tier1, True) + shared_network_vpctier3 = self.create_shared_network_with_associated_network_for_caller(self.normaluser_apiclient, None, vpc3_tier1, True) + shared_network_vpctier4 = self.create_shared_network_with_associated_network_for_caller(self.domainapiclient, self.project, vpc4_tier1, True) + + # Check state of vpc tiers (should be Implemented) + self.check_network_state(self.apiclient, vpc1_tier1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, vpc2_tier1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.normaluser_apiclient, vpc3_tier1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, vpc4_tier1, self.project, NETWORK_STATE_IMPLEMENTED) + + # Delete vpc tiers(should fail) + self.delete_network(vpc1_tier1, self.apiclient, False) + self.delete_network(vpc2_tier1, self.apiclient, False) + self.delete_network(vpc3_tier1, self.apiclient, False) + self.delete_network(vpc4_tier1, self.apiclient, False) + + # Delete shared networks associated to domain admin's isolated network or vpc tier(should succeed) + self.delete_network(shared_network2, self.domainapiclient, True) + self.delete_network(shared_network_vpctier2, self.domainapiclient, True) + + # Wait for network GC to shut down the isolated networks + gc_wait = Configurations.list(self.apiclient, name="network.gc.wait") + gc_interval = Configurations.list(self.apiclient, name="network.gc.interval") + total_sleep = 360 + if gc_wait and gc_interval: + self.logger.debug("network.gc.wait is ==> %s", gc_wait[0].value) + self.logger.debug("network.gc.interval is ==> %s", gc_interval[0].value) + total_sleep = max(int(gc_wait[0].value), int(gc_interval[0].value)) * 2 + 60 + else: + self.logger.debug("Could not retrieve the keys 'network.gc.interval' and 'network.gc.wait'. Sleeping for 6 minutes.") + time.sleep(total_sleep) + + # Check state of isolated networks (1 should be Allocated, 3 should still be Implemented) + self.check_network_state(self.apiclient, isolated_network1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, isolated_network2, None, NETWORK_STATE_ALLOCATED) + self.check_network_state(self.normaluser_apiclient, isolated_network3, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, isolated_network4, self.project, NETWORK_STATE_IMPLEMENTED) + + self.check_network_state(self.apiclient, vpc1_tier1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, vpc2_tier1, None, NETWORK_STATE_ALLOCATED) + self.check_network_state(self.normaluser_apiclient, vpc3_tier1, None, NETWORK_STATE_IMPLEMENTED) + self.check_network_state(self.domainapiclient, vpc4_tier1, self.project, NETWORK_STATE_IMPLEMENTED) + + # Check state of shared network (should be Setup) + self.check_network_state(self.apiclient, shared_network1, None, NETWORK_STATE_SETUP) + self.check_network_state(self.normaluser_apiclient, shared_network3, None, NETWORK_STATE_SETUP) + self.check_network_state(self.domainapiclient, shared_network4, self.project, NETWORK_STATE_SETUP) + + self.check_network_state(self.apiclient, shared_network_vpctier1, None, NETWORK_STATE_SETUP) + self.check_network_state(self.normaluser_apiclient, shared_network_vpctier3, None, NETWORK_STATE_SETUP) + self.check_network_state(self.domainapiclient, shared_network_vpctier4, self.project, NETWORK_STATE_SETUP) + + # Delete admin's shared networks (should succeed) + self.delete_network(shared_network1, self.apiclient, True) + self.delete_network(shared_network_vpctier1, self.apiclient, True) + + # Delete admin's and domain admin's isolated network, but keep the normal user's network + # normal user's shared network and isolated network should be removed in tearDown successfully + self.delete_network(isolated_network1, self.apiclient, True) + self.delete_network(isolated_network2, self.domainapiclient, True) + + self.delete_network(vpc1_tier1, self.apiclient, True) + self.delete_vpc(self.apiclient, vpc1) + self.delete_network(vpc2_tier1, self.domainapiclient, True) + self.delete_vpc(self.domainapiclient, vpc2) diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index b8c1138e846c..93a47b1e0f13 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -3137,7 +3137,7 @@ def create(cls, apiclient, services, accountid=None, domainid=None, networkofferingid=None, projectid=None, subdomainaccess=None, zoneid=None, gateway=None, netmask=None, vpcid=None, aclid=None, vlan=None, - externalid=None, bypassvlanoverlapcheck=None): + externalid=None, bypassvlanoverlapcheck=None, associatednetworkid=None): """Create Network for account""" cmd = createNetwork.createNetworkCmd() cmd.name = services["name"] @@ -3205,6 +3205,8 @@ def create(cls, apiclient, services, accountid=None, domainid=None, cmd.externalid = externalid if bypassvlanoverlapcheck: cmd.bypassvlanoverlapcheck = bypassvlanoverlapcheck + if associatednetworkid: + cmd.associatednetworkid = associatednetworkid return Network(apiclient.createNetwork(cmd).__dict__) def delete(self, apiclient): @@ -4705,14 +4707,15 @@ def __init__(self, items): @classmethod def create(cls, apiclient, gateway, ipaddress, netmask, vlan, vpcid, - physicalnetworkid=None, aclid=None, bypassvlanoverlapcheck=None): + physicalnetworkid=None, aclid=None, bypassvlanoverlapcheck=None, associatednetworkid=None): """Create private gateway""" cmd = createPrivateGateway.createPrivateGatewayCmd() cmd.gateway = gateway cmd.ipaddress = ipaddress cmd.netmask = netmask - cmd.vlan = vlan + if vlan: + cmd.vlan = vlan cmd.vpcid = vpcid if physicalnetworkid: cmd.physicalnetworkid = physicalnetworkid @@ -4720,6 +4723,8 @@ def create(cls, apiclient, gateway, ipaddress, netmask, vlan, vpcid, cmd.aclid = aclid if bypassvlanoverlapcheck: cmd.bypassvlanoverlapcheck = bypassvlanoverlapcheck + if associatednetworkid: + cmd.associatednetworkid = associatednetworkid return PrivateGateway(apiclient.createPrivateGateway(cmd).__dict__) diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 4111e0a74149..21a6ad1d09d1 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -436,6 +436,7 @@ "label.all": "All", "label.all.zone": "All Zones", "label.allocated": "Allocated", +"label.allocatedonly": "Allocated", "label.allocatediops": "IOPS Allocated", "label.allocationstate": "Allocation State", "label.allow": "Allow", @@ -1030,6 +1031,7 @@ "label.guestnetworkname": "Network Name", "label.guestosid": "OS Type", "label.gueststartip": "Guest start IP", +"label.guest.vlan": "Guest VLAN", "label.guestvlanrange": "VLAN Range(s)", "label.guestvmcidr": "CIDR", "label.ha": "HA", @@ -1644,6 +1646,7 @@ "label.physical.network.id": "Physical network ID", "label.physical.network.name": "Physical network name", "label.physicalnetworkid": "Physical Network", +"label.physicalnetworkname": "Physical Network Name", "label.physicalsize": "Physical Size", "label.ping.cifs.password": "PING CIFS password", "label.ping.cifs.username": "PING CIFS username", @@ -2169,6 +2172,7 @@ "label.tag.value": "Tag Value", "label.tagged": "Tagged", "label.tags": "Tags", +"label.taken": "Taken", "label.target.iqn": "Target IQN", "label.tariffactions": "Actions", "label.tariffvalue": "Tariff Value", diff --git a/ui/src/components/view/ListView.vue b/ui/src/components/view/ListView.vue index 7ea3e39a13d1..a7969f9c206b 100644 --- a/ui/src/components/view/ListView.vue +++ b/ui/src/components/view/ListView.vue @@ -185,6 +185,10 @@ + + {{ text }} + {{ text }} + {{ text }} diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index 95c24159bb07..c1fa1ea4f89b 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -38,7 +38,7 @@ export default { return fields }, details: () => { - var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'zonename', 'account', 'domain'] + var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'zonename', 'account', 'domain', 'associatednetworkid', 'associatednetworkname'] if (!isAdmin()) { fields = fields.filter(function (e) { return e !== 'broadcasturi' }) } @@ -685,6 +685,31 @@ export default { groupMap: (selection) => { return selection.map(x => { return { id: x } }) } } ] + }, + { + name: 'guestvlans', + title: 'label.guest.vlan', + icon: 'folder', + permission: ['listGuestVlans'], + resourceType: 'GuestVlan', + filters: ['allocatedonly', 'all'], + columns: ['vlan', 'zonename', 'physicalnetworkname', 'allocationstate', 'taken', 'domain', 'account', 'project'], + details: ['vlan', 'zonename', 'physicalnetworkname', 'allocationstate', 'taken', 'domain', 'account', 'project', 'isdedicated'], + searchFilters: ['zoneid'], + tabs: [{ + name: 'details', + component: () => import('@/components/view/DetailsTab.vue') + }, { + name: 'guest.networks', + component: () => import('@/views/network/GuestVlanNetworksTab.vue'), + show: (record) => { return (record.allocationstate === 'Allocated') } + }], + show: () => { + if (!store.getters.zones || store.getters.zones.length === 0) { + return false + } + return true + } } ] } diff --git a/ui/src/role/index.js b/ui/src/role/index.js index 2052e3e0577e..3385eacb806d 100644 --- a/ui/src/role/index.js +++ b/ui/src/role/index.js @@ -22,5 +22,5 @@ export function isAdmin () { } export function isAdminOrDomainAdmin () { - return ['Admin', 'DomainAdmin'].includes(this.$store.getters.userInfo.roletype) + return ['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype) } diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue index 4c128a780907..22833555f080 100644 --- a/ui/src/views/AutogenView.vue +++ b/ui/src/views/AutogenView.vue @@ -48,7 +48,7 @@ :placeholder="$t('label.filterby')" :value="$route.query.filter || (projectView && $route.name === 'vm' || ['Admin', 'DomainAdmin'].includes($store.getters.userInfo.roletype) && ['vm', 'iso', 'template'].includes($route.name) - ? 'all' : ['guestnetwork'].includes($route.name) ? 'all' : 'self')" + ? 'all' : ['guestnetwork', 'guestvlans'].includes($route.name) ? 'all' : 'self')" style="min-width: 100px; margin-left: 10px" @change="changeFilter" showSearch @@ -1426,6 +1426,12 @@ export default { } } else if (this.$route.name === 'comment') { query.annotationfilter = filter + } else if (this.$route.name === 'guestvlans') { + if (filter === 'all') { + query.allocatedonly = 'false' + } else if (filter === 'allocatedonly') { + query.allocatedonly = 'true' + } } query.filter = filter query.page = 1 diff --git a/ui/src/views/network/CreateNetwork.vue b/ui/src/views/network/CreateNetwork.vue index 56c144932cf3..09045cabd3dc 100644 --- a/ui/src/views/network/CreateNetwork.vue +++ b/ui/src/views/network/CreateNetwork.vue @@ -34,7 +34,7 @@ @refresh-data="refreshParent" @refresh="handleRefresh"/> - + import { api } from '@/api' -import { isAdmin } from '@/role' import CreateIsolatedNetworkForm from '@/views/network/CreateIsolatedNetworkForm' import CreateL2NetworkForm from '@/views/network/CreateL2NetworkForm' import CreateSharedNetworkForm from '@/views/network/CreateSharedNetworkForm' @@ -105,9 +104,6 @@ export default { this.loading = false }) }, - isAdmin () { - return isAdmin() - }, handleRefresh () { }, refreshParent () { diff --git a/ui/src/views/network/CreateSharedNetworkForm.vue b/ui/src/views/network/CreateSharedNetworkForm.vue index 79af46d96a12..1012febbec92 100644 --- a/ui/src/views/network/CreateSharedNetworkForm.vue +++ b/ui/src/views/network/CreateSharedNetworkForm.vue @@ -68,7 +68,7 @@ - + - - - - - - - - - - - - - {{ $t('label.none') }} - - - {{ $t('label.community') }} - - - {{ $t('label.secondary.isolated.vlan.type.isolated') }} - - - {{ $t('label.secondary.isolated.vlan.type.promiscuous') }} - - - - - - - - + {{ $t('label.all') }} - + {{ $t('label.domain') }} @@ -146,7 +106,7 @@ - + - + {{ opt.path || opt.name || opt.description }} - + - + - + + + + + + {{ opt.name || opt.description }} + + + @@ -206,7 +188,7 @@ - + {{ opt.name || opt.description }} @@ -236,6 +218,67 @@ + + + + + + + + + + + + + {{ $t('label.none') }} + + + {{ $t('label.community') }} + + + {{ $t('label.secondary.isolated.vlan.type.isolated') }} + + + {{ $t('label.secondary.isolated.vlan.type.promiscuous') }} + + + + + + + + + + + + + + + {{ opt.name || opt.description }} + + + + @@ -272,7 +315,7 @@ - + - + import { api } from '@/api' +import { isAdmin, isAdminOrDomainAdmin } from '@/role' import ResourceIcon from '@/components/view/ResourceIcon' import TooltipLabel from '@/components/widgets/TooltipLabel' @@ -370,10 +414,6 @@ export default { type: Object, default: null }, - physicalNetworks: { - type: Array, - default: null - }, resource: { type: Object, default: () => { return {} } @@ -396,6 +436,12 @@ export default { networkOfferings: [], networkOfferingLoading: false, selectedNetworkOffering: {}, + networks: [], + networkLoading: false, + selectedNetwork: {}, + accounts: [], + accountLoading: false, + selectedAccount: {}, projects: [], projectLoading: false, selectedProject: {}, @@ -423,6 +469,12 @@ export default { this.fetchNetworkOfferingData() } }, + isAdmin () { + return isAdmin() + }, + isAdminOrDomainAdmin () { + return isAdminOrDomainAdmin() + }, isObjectEmpty (obj) { return !(obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object) }, @@ -476,43 +528,42 @@ export default { }, handleZoneChange (zone) { this.selectedZone = zone - this.fetchPhysicalNetworkData() + if (isAdmin()) { + this.fetchPhysicalNetworkData() + } else { + this.fetchNetworkOfferingData() + } }, fetchPhysicalNetworkData () { this.formSelectedPhysicalNetwork = {} this.formPhysicalNetworks = [] - if (this.physicalNetworks != null) { - this.formPhysicalNetworks = this.physicalNetworks - this.selectFirstPhysicalNetwork() - } else { - if (this.selectedZone === null || this.selectedZone === undefined) { - return - } - const promises = [] - const params = { - zoneid: this.selectedZone.id + if (this.selectedZone === null || this.selectedZone === undefined) { + return + } + const promises = [] + const params = { + zoneid: this.selectedZone.id + } + this.formPhysicalNetworkLoading = true + api('listPhysicalNetworks', params).then(json => { + var networks = json.listphysicalnetworksresponse.physicalnetwork + if (this.arrayHasItems(networks)) { + for (const network of networks) { + promises.push(this.addPhysicalNetworkForGuestTrafficType(network)) + } + } else { + this.formPhysicalNetworkLoading = false } - this.formPhysicalNetworkLoading = true - api('listPhysicalNetworks', params).then(json => { - var networks = json.listphysicalnetworksresponse.physicalnetwork - if (this.arrayHasItems(networks)) { - for (const network of networks) { - promises.push(this.addPhysicalNetworkForGuestTrafficType(network)) - } - } else { + }).finally(() => { + if (this.arrayHasItems(promises)) { + Promise.all(promises).catch(error => { + this.$notifyError(error) + }).finally(() => { this.formPhysicalNetworkLoading = false - } - }).finally(() => { - if (this.arrayHasItems(promises)) { - Promise.all(promises).catch(error => { - this.$notifyError(error) - }).finally(() => { - this.formPhysicalNetworkLoading = false - this.selectFirstPhysicalNetwork() - }) - } - }) - } + this.selectFirstPhysicalNetwork() + }) + } + }) }, selectFirstPhysicalNetwork () { if (this.arrayHasItems(this.formPhysicalNetworks)) { @@ -559,14 +610,31 @@ export default { } case 'project': { - this.fetchDomainData() - this.fetchProjectData() - this.fetchNetworkOfferingData() + if (isAdminOrDomainAdmin()) { + this.fetchDomainData() + } else { + this.fetchNetworkOfferingData() + this.fetchProjectData() + } + break + } + case 'account': + { + if (isAdminOrDomainAdmin()) { + this.fetchDomainData() + } else { + this.fetchNetworkOfferingData() + this.fetchAccountData() + } break } default: { - this.fetchNetworkOfferingData() + if (isAdminOrDomainAdmin()) { + this.fetchDomainData() + } else { + this.fetchNetworkOfferingData() + } } } }, @@ -579,6 +647,9 @@ export default { zoneid: this.selectedZone.id, state: 'Enabled' } + if (!isAdmin()) { + params.specifyvlan = false + } if (!this.isObjectEmpty(this.formSelectedPhysicalNetwork) && this.formSelectedPhysicalNetwork.tags && this.formSelectedPhysicalNetwork.tags.length > 0) { @@ -586,11 +657,9 @@ export default { } // Network tab in Guest Traffic Type in Infrastructure menu is only available when it's under Advanced zone. // zone dropdown in add guest network dialog includes only Advanced zones. - if (this.scopeType === 'all' || this.scopeType === 'domain') { - params.guestiptype = 'Shared' - if (this.scopeType === 'domain') { - params.domainid = this.selectedDomain.id - } + params.guestiptype = 'Shared' + if (this.scopeType === 'domain') { + params.domainid = this.selectedDomain.id } this.handleNetworkOfferingChange(null) this.networkOfferings = [] @@ -616,8 +685,60 @@ export default { this.selectedNetworkOffering = networkOffering if (networkOffering) { this.networkServiceProviderMap(this.selectedNetworkOffering.id) + if (!networkOffering.specifyvlan) { + this.fetchNetworkData() + } } }, + fetchNetworkData () { + if (this.isObjectEmpty(this.selectedZone)) { + return + } + if (this.isObjectEmpty(this.selectedNetworkOffering) || this.selectedNetworkOffering.specifyvlan) { + return + } + this.networkLoading = true + var params = { + zoneid: this.selectedZone.id, + type: 'Isolated' + } + if (this.formSelectedPhysicalNetwork) { + params.physicalnetworkid = this.formSelectedPhysicalNetwork.id + } + switch (this.scopeType) { + case 'domain': + { + params.domainid = this.selectedDomain.id + break + } + case 'project': + { + params.projectid = this.selectedProject.id + break + } + case 'account': + { + params.domainid = this.selectedDomain.id + params.account = this.selectedAccount.name + break + } + default: + { + } + } + this.handleNetworkChange(null) + this.networks = [] + api('listNetworks', params).then(json => { + this.networks = json.listnetworksresponse.network + }).catch(error => { + this.$notifyError(error) + }).finally(() => { + this.networkLoading = false + }) + }, + handleNetworkChange (selectedNetwork) { + this.selectedNetwork = selectedNetwork + }, networkServiceProviderMap (id) { api('listNetworkOfferings', { id: id }).then(json => { var networkOffering = json.listnetworkofferingsresponse.networkoffering[0] @@ -639,7 +760,7 @@ export default { fetchDomainData () { const params = {} if (!this.isObjectEmpty(this.selectedZone) && this.selectedZone.domainid != null) { - params.id = this.selectedZone.id + params.id = this.selectedZone.domainid params.isrecursive = true } else { params.listall = true @@ -648,19 +769,52 @@ export default { this.domainLoading = true api('listDomains', params).then(json => { const listDomains = json.listdomainsresponse.domain - this.domains = this.domains.concat(listDomains) + this.domains = listDomains }).finally(() => { this.domainLoading = false - this.form.setFieldsValue({ - domainid: 0 - }) - this.handleDomainChange(this.domains[0]) + if (this.arrayHasItems(this.domains) && this.scopeType !== 'all') { + this.form.setFieldsValue({ + domainid: 0 + }) + this.handleDomainChange(this.domains[0]) + } }) }, handleDomainChange (domain) { this.selectedDomain = domain if (!this.isObjectEmpty(domain)) { this.fetchNetworkOfferingData() + this.fetchAccountData() + this.fetchProjectData() + this.fetchNetworkData() + } + }, + fetchAccountData () { + this.accounts = [] + const params = {} + params.showicon = true + params.details = 'min' + if (this.selectedDomain) { + params.domainid = this.selectedDomain.id + } + this.accountLoading = true + api('listAccounts', params).then(json => { + const listaccounts = json.listaccountsresponse.account + this.accounts = listaccounts + }).finally(() => { + this.accountLoading = false + if (this.arrayHasItems(this.accounts) && this.scopeType === 'account') { + this.form.setFieldsValue({ + accountid: 0 + }) + this.handleAccountChange(this.accounts[0]) + } + }) + }, + handleAccountChange (account) { + this.selectedAccount = account + if (!this.isObjectEmpty(account)) { + this.fetchNetworkOfferingData() } }, fetchProjectData () { @@ -669,13 +823,16 @@ export default { params.listall = true params.showicon = true params.details = 'min' + if (this.selectedDomain) { + params.domainid = this.selectedDomain.id + } this.projectLoading = true api('listProjects', params).then(json => { const listProjects = json.listprojectsresponse.project this.projects = this.projects.concat(listProjects) }).finally(() => { this.projectLoading = false - if (this.arrayHasItems(this.projects)) { + if (this.arrayHasItems(this.projects) && this.scopeType === 'project') { this.form.setFieldsValue({ projectid: 0 }) @@ -731,12 +888,16 @@ export default { params.isolatedpvlan = values.isolatedpvlan } } + if (this.selectedNetwork) { + params.associatednetworkid = this.selectedNetwork.id + } if (this.scopeType !== 'all') { params.domainid = this.selectedDomain.id params.acltype = this.scopeType if (this.scopeType === 'account') { // account-specific - params.account = values.account + params.account = this.selectedAccount.name } else if (this.scopeType === 'project') { // project-specific + params.acltype = 'account' params.projectid = this.selectedProject.id } else { // domain-specific params.subdomainaccess = this.parseBooleanValueForKey(values, 'subdomainaccess') diff --git a/ui/src/views/network/GuestVlanNetworksTab.vue b/ui/src/views/network/GuestVlanNetworksTab.vue new file mode 100644 index 000000000000..b2811731a4ec --- /dev/null +++ b/ui/src/views/network/GuestVlanNetworksTab.vue @@ -0,0 +1,136 @@ +// 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. + + + + + diff --git a/ui/src/views/network/VpcTab.vue b/ui/src/views/network/VpcTab.vue index 7d5fedcad025..38a9e4109b0b 100644 --- a/ui/src/views/network/VpcTab.vue +++ b/ui/src/views/network/VpcTab.vue @@ -143,7 +143,7 @@

{{ $t('message.add.new.gateway.to.vpc') }}

- + - + + v-if="this.isAdmin()"> + + + + + + + {{ opt.name || opt.description }} + + + + { this.fetchLoading = false }) + api('listNetworks', { + domainid: this.resource.domainid, + account: this.resource.account, + listAll: true, + type: 'Isolated' + }).then(json => { + this.isolatedNetworks = json.listnetworksresponse.network + }).catch(error => { + this.$notifyError(error) + }) }, fetchVpnGateways () { this.fetchLoading = true @@ -585,6 +617,10 @@ export default { }, fetchPhysicalNetworks () { this.modals.gatewayLoading = true + if (!this.isAdmin()) { + this.modals.gatewayLoading = false + return + } api('listPhysicalNetworks', { zoneid: this.resource.zoneid }).then(json => { this.physicalnetworks = json.listphysicalnetworksresponse.physicalnetwork if (this.modals.gateway === true) { @@ -651,12 +687,17 @@ export default { ipaddress: data.ipaddress, gateway: data.gateway, netmask: data.netmask, - vlan: data.vlan, aclid: data.acl } if (data.bypassvlanoverlapcheck) { params.bypassvlanoverlapcheck = data.bypassvlanoverlapcheck } + if (data.vlan && String(data.vlan).length > 0) { + params.vlan = data.vlan + } + if (data.associatednetwork) { + params.associatednetworkid = data.associatednetwork + } api('createPrivateGateway', params).then(response => { this.$pollJob({ diff --git a/ui/src/views/offering/AddNetworkOffering.vue b/ui/src/views/offering/AddNetworkOffering.vue index 7b2350ee543e..fb804a2d024d 100644 --- a/ui/src/views/offering/AddNetworkOffering.vue +++ b/ui/src/views/offering/AddNetworkOffering.vue @@ -73,17 +73,17 @@ - + - - + + - - - + + + @@ -741,7 +741,9 @@ export default { }) if (values.guestiptype === 'shared') { // specifyVlan checkbox is disabled, so inputData won't include specifyVlan - params.specifyvlan = true + if (values.specifyvlan === true) { + params.specifyvlan = true + } params.specifyipranges = true delete params.ispersistent } else if (values.guestiptype === 'isolated') { // specifyVlan checkbox is shown