From a5014a28a6eb77fc6131eb6ab11422c1657197fb Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 19 Apr 2022 16:29:31 +0200 Subject: [PATCH] New feature: give access permission of networks to other accounts in same domain (#5769) * Enhancement: create Shared networks and VPC private gateways by users * UI bug fix: pass correct domainid in CreateSharedNetworkForm * Update #5730: fix test failure with test_guest_vlan_range.py * Update #5730: fix test failure with test_persistent_network.py * Update #5730: Add since to new API commands and API parameters * Update #5730: Get first physical network for VPC private gateway if other ways do not work * Update #5730: code optimization (return !offering.isSpecifyVlan()) * Update #5730: fix hard-coded network offering id in test_pvlan.py * Update #5730: skip access check on the network owner if the owner is ROOT/system * Update #5730: overlap check on cidr/startip/endip * Update #5730: add methods to get accountid/domainid of shared networks * Update #5730: improve integration tests * Update #5730: update as per GutoVeronezi's comments * Network Sharing: give network access permission to other accounts within a domain * network: update ip in lb/pf/dnat tables when update vm nic ip * Update #5757: create 3 separated methods for DNAT/LB/PF update * travis: install python3-setuptools * Network Sharing: update integration test * Update #5769: Remove NetworkPermission.Ops * Update #5769: Update as per Daan's comments * Update #5769: Update as per Suresh's comments * Update #5769: fix UI bug that accounts/projects are not listed * Update #5769: fix domain admin can deploy vm on L2 network of other users * Update #5769: Remove method listPermittedNetworkIdsByDomains in NetworkPermissionDao * Update #5769: Skip network operation permissions check for root admin * UI: fix create Isolated/L2 network form * Update #5730: fix create Shared network form * Update #5769: fix domain admin can deploy vm on L2 network of other users * test: fix test_storage_policy.py * Update #5769: fix remove_nic in test_network_permissions.py * Update #5769: extract some codes to a method * Update #5769: fix add/remove nic by domain admin * Update #5769: allow domain admin to enable/disable static nat and create port forwarding rules * Update #5769: update integration test * Update #5769: fix unit test AssignLoadBalancerTest.java * Update #5769: allow normal users to share network permission to other users on UI * Update #5769: fix small UI bug with label * Update #5769: Support L2 network as associated network * test: sleep 30s after restarting mgt server in test_kubernetes_supported_versions.py to fix test failures with test_secondary_storage.py * Update #5784: revert part of changes in #2420 * Update #5757: invert if condition to reduce code indentation * Update #5769: fix regular user cannot create L2 network * Update #5769: Add associated nework id and name in private gateway response * Update #5769: list networks by networkfilter=Account on UI * Update #5769: fix ui issue when list private gateways or create shared network if no isolated networks * Update #5769: fix vue ui warnings * Update #5679: add BaseResponseWithAssociatedNetwork and extract method setResponseAssociatedNetworkInformation * Update #5679: extract some methods in VpcManagerImpl.java * Update #5679: Update smoke tests as per Daan's comments * Update #5769: fix vpc with private gateways cannot be removed when remove an acount * Update #5769: fix unit test failures after merging latest main * Update #5769: fix schema-41610to41700.sql * Update #5769: fix Request failed due to empty network offering list on UI * Update #5769: Throw exception when account is not found by name * Update #5769: display a warning message if network offering list is empty * Update #5769: fix an UI bug caused by previous commit b286cb76774e301d15cf2b3b6ea316eaa306018f * Update #5769: fix UI bugs due to vue3 merge * Update #5769: fix issue due to account type refactoring * Update #5769: fix ui bugs due to vue3 * Update #5769: fix issue due to vue3 upgrade * Update #5769: fix issue due to vue3 upgrade part 2 * Update #5769: fix issue due to vue3 upgrade part 3 * Update #5769: highlight default scope when create shared network on UI * Update #5769: fix domain list is not loaded on UI * Update #5769: fix restart/delete shared network by normal users * Update #5769: fix restart domain-scope shared network by domain admin * Update #5769: fix 3 UI bugs (1) double networks in list; (2) icon of first items in list; (3) account/project autoselect * Update #5769: fix 2 ui bugs; (1) selected project is not changed when change domain; (2) no network should be selected by default * Update #5769: fix update shared networks by domain admin/regular user * Update #5769: fix Flicking warning message about the empty network offerings * Update #5769: display associated network name in shared network info card * Update #5769: fix create private gateway form * Update #5769: fix network lists in project view * Update #5769: fix duplicated networks in network dropdown * Update #5769: fix failed to create shared network if associated L2 network is Setup * Update #5769: check AccessType.OperateEntry on network in its implementation * Revert "Update #5769: check AccessType.OperateEntry on network in its implementation" This reverts commit c42c489e5bb26e2c780ac1be79bc8ca299a23f6e. * Update #5769: fix keyword search in list guest vlans --- .../java/com/cloud/network/GuestVlan.java | 19 +- .../com/cloud/network/GuestVlanRange.java | 32 + .../main/java/com/cloud/network/Network.java | 10 + .../java/com/cloud/network/NetworkModel.java | 5 + .../com/cloud/network/NetworkPermission.java | 26 + .../com/cloud/network/NetworkService.java | 21 +- .../com/cloud/network/vpc/VpcService.java | 4 +- .../com/cloud/offering/NetworkOffering.java | 1 + .../apache/cloudstack/api/ApiConstants.java | 6 + .../BaseResponseWithAssociatedNetwork.java | 40 + .../cloudstack/api/ResponseGenerator.java | 10 +- .../network/DedicateGuestVlanRangeCmd.java | 4 +- .../ListDedicatedGuestVlanRangesCmd.java | 6 +- .../admin/network/ListGuestVlansCmd.java | 113 +++ .../admin/network/ListNetworksCmdByAdmin.java | 16 +- .../vpc/CreatePrivateGatewayByAdminCmd.java | 74 ++ .../admin/vpc/DeletePrivateGatewayCmd.java | 4 +- .../user/network/CreateNetworkCmd.java | 35 +- .../network/CreateNetworkPermissionsCmd.java | 130 +++ .../user/network/DeleteNetworkCmd.java | 3 +- .../network/ListNetworkPermissionsCmd.java | 94 +++ .../command/user/network/ListNetworksCmd.java | 26 + .../network/RemoveNetworkPermissionsCmd.java | 129 +++ .../network/ResetNetworkPermissionsCmd.java | 89 ++ .../user/network/RestartNetworkCmd.java | 11 + .../user/network/UpdateNetworkCmd.java | 11 + .../vpc/CreatePrivateGatewayCmd.java | 64 +- .../api/response/GuestVlanRangeResponse.java | 4 +- .../api/response/GuestVlanResponse.java | 156 ++++ .../response/NetworkPermissionsResponse.java | 87 ++ .../api/response/NetworkResponse.java | 20 +- .../api/response/PrivateGatewayResponse.java | 5 +- .../service/NetworkOrchestrationService.java | 2 + .../orchestration/NetworkOrchestrator.java | 44 +- .../java/com/cloud/dc/DataCenterVnetVO.java | 9 +- .../src/main/java/com/cloud/dc/VlanVO.java | 2 +- .../network/dao/AccountGuestVlanMapVO.java | 4 +- .../cloud/offerings/NetworkOfferingVO.java | 4 +- .../com/cloud/vm/dao/DomainRouterDaoImpl.java | 3 +- .../network/NetworkPermissionVO.java | 61 ++ .../network/dao/NetworkPermissionDao.java | 65 ++ .../network/dao/NetworkPermissionDaoImpl.java | 98 +++ ...spring-engine-schema-core-daos-context.xml | 1 + .../META-INF/db/schema-41610to41700.sql | 9 + .../java/com/cloud/acl/DomainChecker.java | 5 + .../java/com/cloud/api/ApiResponseHelper.java | 116 ++- .../api/dispatch/ParamProcessWorker.java | 11 +- .../ConfigurationManagerImpl.java | 142 ++-- .../com/cloud/network/NetworkModelImpl.java | 162 +++- .../com/cloud/network/NetworkServiceImpl.java | 599 ++++++++++++-- .../cloud/network/guru/GuestNetworkGuru.java | 2 +- .../network/guru/PrivateNetworkGuru.java | 2 +- .../lb/LoadBalancingRulesManagerImpl.java | 7 +- .../cloud/network/rules/RulesManagerImpl.java | 18 +- .../com/cloud/network/vpc/VpcManagerImpl.java | 279 ++++--- .../cloud/server/ConfigurationServerImpl.java | 4 +- .../cloud/server/ManagementServerImpl.java | 23 +- .../com/cloud/user/AccountManagerImpl.java | 22 +- .../java/com/cloud/vm/UserVmManagerImpl.java | 11 +- .../network/CreatePrivateNetworkTest.java | 14 +- .../network/DedicateGuestVlanRangesTest.java | 18 +- .../cloud/network/MockNetworkModelImpl.java | 11 + .../network/UpdatePhysicalNetworkTest.java | 2 +- .../network/lb/AssignLoadBalancerTest.java | 54 +- .../com/cloud/vpc/MockNetworkManagerImpl.java | 43 +- .../com/cloud/vpc/MockNetworkModelImpl.java | 11 + .../CreateNetworkOfferingTest.java | 4 +- .../privategw/AclOnPrivateGwTest.java | 2 +- .../smoke/test_network_permissions.py | 760 ++++++++++++++++++ test/integration/smoke/test_pvlan.py | 17 +- .../smoke/test_user_private_gateway.py | 425 ++++++++++ .../smoke/test_user_shared_network.py | 631 +++++++++++++++ tools/marvin/marvin/lib/base.py | 58 +- ui/public/locales/en.json | 14 + ui/src/components/view/InfoCard.vue | 2 +- ui/src/components/view/ListView.vue | 5 + ui/src/components/view/SearchView.vue | 29 +- ui/src/config/section/network.js | 37 +- ui/src/views/AutogenView.vue | 12 +- ui/src/views/network/CreateNetwork.vue | 6 +- .../views/network/CreateNetworkPermission.vue | 247 ++++++ .../views/network/CreateSharedNetworkForm.vue | 356 ++++++-- ui/src/views/network/GuestVlanNetworksTab.vue | 135 ++++ ui/src/views/network/NetworkPermissions.vue | 207 +++++ ui/src/views/network/VpcTab.vue | 59 +- ui/src/views/offering/AddNetworkOffering.vue | 22 +- 86 files changed, 5599 insertions(+), 542 deletions(-) create mode 100644 api/src/main/java/com/cloud/network/GuestVlanRange.java create mode 100644 api/src/main/java/com/cloud/network/NetworkPermission.java create mode 100755 api/src/main/java/org/apache/cloudstack/api/BaseResponseWithAssociatedNetwork.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/admin/network/ListGuestVlansCmd.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayByAdminCmd.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkPermissionsCmd.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkPermissionsCmd.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/user/network/RemoveNetworkPermissionsCmd.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/command/user/network/ResetNetworkPermissionsCmd.java rename api/src/main/java/org/apache/cloudstack/api/command/{admin => user}/vpc/CreatePrivateGatewayCmd.java (81%) create mode 100644 api/src/main/java/org/apache/cloudstack/api/response/GuestVlanResponse.java create mode 100644 api/src/main/java/org/apache/cloudstack/api/response/NetworkPermissionsResponse.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/network/NetworkPermissionVO.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/network/dao/NetworkPermissionDao.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/network/dao/NetworkPermissionDaoImpl.java create mode 100644 test/integration/smoke/test_network_permissions.py create mode 100644 test/integration/smoke/test_user_private_gateway.py create mode 100644 test/integration/smoke/test_user_shared_network.py create mode 100644 ui/src/views/network/CreateNetworkPermission.vue create mode 100644 ui/src/views/network/GuestVlanNetworksTab.vue create mode 100644 ui/src/views/network/NetworkPermissions.vue diff --git a/api/src/main/java/com/cloud/network/GuestVlan.java b/api/src/main/java/com/cloud/network/GuestVlan.java index 4597192ef60..dff74ddaee6 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(); - public String getGuestVlanRange(); + String getVnet(); - public long getPhysicalNetworkId(); + String getReservationId(); + + Long getAccountId(); + + long getDataCenterId(); + + 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 00000000000..adc5addf890 --- /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 1940cd0e020..cdbdf6b4a72 100644 --- a/api/src/main/java/com/cloud/network/Network.java +++ b/api/src/main/java/com/cloud/network/Network.java @@ -332,6 +332,14 @@ public interface Network extends ControlledEntity, StateObject, I } } + public enum NetworkFilter { + Account, // return account networks that have been registered for or created by the calling user + Domain, // return domain networks that have been registered for or created by the calling user + AccountDomain, // return account and domain networks that have been registered for or created by the calling user + Shared, // including networks that have been granted to the calling user by another user + All // all networks (account, domain and shared) + } + public class IpAddresses { private String ip4Address; private String ip6Address; @@ -372,6 +380,8 @@ public interface Network extends ControlledEntity, StateObject, I } } + 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 e933a1cc7bd..971bb308b1d 100644 --- a/api/src/main/java/com/cloud/network/NetworkModel.java +++ b/api/src/main/java/com/cloud/network/NetworkModel.java @@ -35,6 +35,7 @@ import com.cloud.network.Network.Service; import com.cloud.network.Networks.TrafficType; import com.cloud.network.element.NetworkElement; import com.cloud.network.element.UserDataServiceProvider; +import com.cloud.network.router.VirtualRouter; import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Detail; import com.cloud.user.Account; @@ -195,6 +196,10 @@ public interface NetworkModel { void checkNetworkPermissions(Account owner, Network network); + void checkNetworkOperatePermissions(Account owner, Network network); + + void checkRouterPermissions(Account owner, VirtualRouter router); + String getDefaultManagementTrafficLabel(long zoneId, HypervisorType hypervisorType); String getDefaultStorageTrafficLabel(long zoneId, HypervisorType hypervisorType); diff --git a/api/src/main/java/com/cloud/network/NetworkPermission.java b/api/src/main/java/com/cloud/network/NetworkPermission.java new file mode 100644 index 00000000000..d42a299ffff --- /dev/null +++ b/api/src/main/java/com/cloud/network/NetworkPermission.java @@ -0,0 +1,26 @@ +// 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.InternalIdentity; + +public interface NetworkPermission extends InternalIdentity { + + long getNetworkId(); + + long getAccountId(); +} diff --git a/api/src/main/java/com/cloud/network/NetworkService.java b/api/src/main/java/com/cloud/network/NetworkService.java index fe89cb5e640..4e4b0d4d927 100644 --- a/api/src/main/java/com/cloud/network/NetworkService.java +++ b/api/src/main/java/com/cloud/network/NetworkService.java @@ -22,9 +22,14 @@ import java.util.Map; 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.CreateNetworkPermissionsCmd; +import org.apache.cloudstack.api.command.user.network.ListNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; +import org.apache.cloudstack.api.command.user.network.RemoveNetworkPermissionsCmd; +import org.apache.cloudstack.api.command.user.network.ResetNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd; import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd; import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; @@ -148,9 +153,9 @@ public interface NetworkService { 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); @@ -184,7 +189,7 @@ public interface NetworkService { * @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; /** @@ -210,4 +215,14 @@ public interface NetworkService { AcquirePodIpCmdResponse allocatePodIp(Account account, String zoneId, String podId) throws ResourceAllocationException, ConcurrentOperationException; boolean releasePodIp(ReleasePodIpCmdByAdmin ip) throws CloudRuntimeException; + + Pair, Integer> listGuestVlans(ListGuestVlansCmd cmd); + + List listNetworkPermissions(ListNetworkPermissionsCmd listNetworkPermissionsCmd); + + boolean createNetworkPermissions(CreateNetworkPermissionsCmd createNetworkPermissionsCmd); + + boolean removeNetworkPermissions(RemoveNetworkPermissionsCmd removeNetworkPermissionsCmd); + + boolean resetNetworkPermissions(ResetNetworkPermissionsCmd resetNetworkPermissionsCmd); } 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 d7c83f91678..4e993a71968 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 @@ package com.cloud.network.vpc; 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 interface VpcService { * @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 594938775c8..eaff1721634 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 interface NetworkOffering extends InfrastructureEntity, InternalIdentity, 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 698d5d88213..788f46aba88 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -21,6 +21,7 @@ public class ApiConstants { public static final String ACCOUNTS = "accounts"; public static final String ACCOUNT_TYPE = "accounttype"; public static final String ACCOUNT_ID = "accountid"; + public static final String ACCOUNT_IDS = "accountids"; public static final String ACCUMULATE = "accumulate"; public static final String ACTIVITY = "activity"; public static final String ADAPTER_TYPE = "adaptertype"; @@ -422,6 +423,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"; @@ -441,6 +444,7 @@ public class ApiConstants { public static final String TIER_NETWORK_OFFERINGS = "tiernetworkofferings"; public static final String NETWORK_IDS = "networkids"; public static final String NETWORK_ID = "networkid"; + public static final String NETWORK_FILTER = "networkfilter"; public static final String NIC_ID = "nicid"; public static final String SPECIFY_VLAN = "specifyvlan"; public static final String IS_DEFAULT = "isdefault"; @@ -536,6 +540,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"; @@ -572,6 +577,7 @@ public class ApiConstants { public static final String FIREWALL_DEVICE_CAPACITY = "fwdevicecapacity"; public static final String FIREWALL_DEVICE_DEDICATED = "fwdevicededicated"; public static final String SERVICE = "service"; + public static final String ASSOCIATED_NETWORK = "associatednetwork"; public static final String ASSOCIATED_NETWORK_ID = "associatednetworkid"; public static final String ASSOCIATED_NETWORK_NAME = "associatednetworkname"; public static final String SOURCE_NAT_SUPPORTED = "sourcenatsupported"; diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithAssociatedNetwork.java b/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithAssociatedNetwork.java new file mode 100755 index 00000000000..1ffe4657bd9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/BaseResponseWithAssociatedNetwork.java @@ -0,0 +1,40 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public abstract class BaseResponseWithAssociatedNetwork extends BaseResponseWithAnnotations { + + @SerializedName(ApiConstants.ASSOCIATED_NETWORK_ID) + @Param(description = "the ID of the Network associated with this private gateway") + private String associatedNetworkId; + + @SerializedName(ApiConstants.ASSOCIATED_NETWORK) + @Param(description = "the name of the Network associated with this private gateway") + private String associatedNetworkName; + + public void setAssociatedNetworkId(String associatedNetworkId) { + this.associatedNetworkId = associatedNetworkId; + } + + public void setAssociatedNetworkName(String associatedNetworkName) { + this.associatedNetworkName = associatedNetworkName; + } + +} 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 6e149b60edf..cdf375523de 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -27,6 +27,8 @@ import com.cloud.utils.Pair; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.DirectDownloadCertificateHostStatusResponse; +import org.apache.cloudstack.api.response.GuestVlanResponse; +import org.apache.cloudstack.api.response.NetworkPermissionsResponse; import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse; import com.cloud.resource.RollingMaintenanceManager; import org.apache.cloudstack.api.response.RollingMaintenanceResponse; @@ -154,9 +156,11 @@ import com.cloud.event.Event; 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; +import com.cloud.network.NetworkPermission; import com.cloud.network.Networks.IsolationType; import com.cloud.network.OvsProvider; import com.cloud.network.PhysicalNetwork; @@ -262,7 +266,7 @@ public interface ResponseGenerator { IPAddressResponse createIPAddressResponse(ResponseView view, IpAddress ipAddress); - GuestVlanRangeResponse createDedicatedGuestVlanRangeResponse(GuestVlan result); + GuestVlanRangeResponse createDedicatedGuestVlanRangeResponse(GuestVlanRange result); GlobalLoadBalancerResponse createGlobalLoadBalancerResponse(GlobalLoadBalancerRule globalLoadBalancerRule); @@ -497,6 +501,10 @@ public interface ResponseGenerator { ResourceIconResponse createResourceIconResponse(ResourceIcon resourceIcon); + GuestVlanResponse createGuestVlanResponse(GuestVlan vlan); + + NetworkPermissionsResponse createNetworkPermissionsResponse(NetworkPermission permission); + DirectDownloadCertificateResponse createDirectDownloadCertificateResponse(DirectDownloadCertificate certificate); List createDirectDownloadCertificateHostMapResponse(List hostMappings); 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 ba7b4b99c25..e8fc7b383b9 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 org.apache.cloudstack.api.response.ProjectResponse; 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 class DedicateGuestVlanRangeCmd extends BaseCmd { @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 9b1935c1d5a..617a07b5c8c 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.PhysicalNetworkResponse; 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 class ListDedicatedGuestVlanRangesCmd extends BaseListCmd { @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 00000000000..dc9df12467b --- /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 a234ff07886..8df1133d5e7 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 @@ import com.cloud.network.Network; @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 00000000000..b42e3d95285 --- /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 8d67a4ed8c5..1126779f45b 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 @@ package org.apache.cloudstack.api.command.admin.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; @@ -37,7 +38,8 @@ import com.cloud.network.vpc.VpcGateway; 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 5dff7c9bf2b..10aadee40e6 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 class CreateNetworkCmd extends BaseCmd implements UserCmd { return isolatedPvlanType; } + public Long getAssociatedNetworkId() { + return associatedNetworkId; + } + @Override public boolean isDisplay() { if(displayNetwork == null) @@ -249,11 +260,31 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { 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/CreateNetworkPermissionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkPermissionsCmd.java new file mode 100644 index 00000000000..8224562da76 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkPermissionsCmd.java @@ -0,0 +1,130 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.network; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.Network; +import com.cloud.user.Account; + +import java.util.List; + +@APICommand(name = CreateNetworkPermissionsCmd.APINAME, description = "Updates network permissions.", + responseObject = SuccessResponse.class, + entityType = {Network.class}, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.17.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreateNetworkPermissionsCmd extends BaseCmd { + public static final Logger LOGGER = Logger.getLogger(CreateNetworkPermissionsCmd.class.getName()); + + public static final String APINAME = "createNetworkPermissions"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ACCOUNTS, + type = CommandType.LIST, + collectionType = CommandType.STRING, + description = "a comma delimited list of accounts within owner's domain. If specified, \"op\" parameter has to be passed in.") + private List accountNames; + + @Parameter(name = ApiConstants.ACCOUNT_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = AccountResponse.class, + description = "a comma delimited list of account IDs within owner's domain. If specified, \"op\" parameter has to be passed in.") + private List accountIds; + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the network ID") + private Long networkId; + + @Parameter(name = ApiConstants.PROJECT_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = ProjectResponse.class, + description = "a comma delimited list of projects within owner's domain. If specified, \"op\" parameter has to be passed in.") + private List projectIds; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + + public List getAccountNames() { + return accountNames; + } + + public List getAccountIds() { + return accountIds; + } + + public Long getNetworkId() { + return networkId; + } + + public List getProjectIds() { + return projectIds; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public void execute() { + if (accountIds != null && accountNames != null) { + throw new InvalidParameterValueException("Accounts and accountNames can't be specified together"); + } + boolean result = _networkService.createNetworkPermissions(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update network permissions"); + } + } + + @Override + public long getEntityOwnerId() { + Network network = _entityMgr.findById(Network.class, getNetworkId()); + if (network != null) { + return network.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } +} 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 72a953857af..c8e49f414f3 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/ListNetworkPermissionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkPermissionsCmd.java new file mode 100644 index 00000000000..ea5034b1be7 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ListNetworkPermissionsCmd.java @@ -0,0 +1,94 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.network; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkPermissionsResponse; +import org.apache.cloudstack.api.response.NetworkResponse; + +import com.cloud.network.Network; +import com.cloud.network.NetworkPermission; +import com.cloud.user.Account; + +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = ListNetworkPermissionsCmd.APINAME, description = "List network visibility and all accounts that have permissions to view this network.", + responseObject = NetworkPermissionsResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.17.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListNetworkPermissionsCmd extends BaseCmd implements UserCmd { + public static final Logger LOGGER = Logger.getLogger(ListNetworkPermissionsCmd.class.getName()); + + public static final String APINAME = "listNetworkPermissions"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "Lists network permission by network ID") + private Long networkId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getNetworkId() { + return networkId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public long getEntityOwnerId() { + Network Network = _entityMgr.findById(Network.class, getNetworkId()); + if (Network != null) { + return Network.getAccountId(); + } + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public void execute() { + List permissions = _networkService.listNetworkPermissions(this); + ListResponse response = new ListResponse<>(); + List networkPermissionResponses = new ArrayList<>(); + for (NetworkPermission permission : permissions) { + NetworkPermissionsResponse networkPermissionResponse = _responseGenerator.createNetworkPermissionsResponse(permission); + networkPermissionResponses.add(networkPermissionResponse); + } + response.setResponses(networkPermissionResponses, permissions.size()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} 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 315a58a2f5a..df82d9fd625 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,10 +96,28 @@ 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; + @Parameter(name = ApiConstants.NETWORK_FILTER, + type = CommandType.STRING, + since = "4.17.0", + description = "possible values are \"account\", \"domain\", \"accountdomain\",\"shared\", and \"all\". Default value is \"all\"." + + "* account : account networks that have been registered for or created by the calling user. " + + "* domain : domain networks that have been registered for or created by the calling user. " + + "* accountdomain : account and domain networks that have been registered for or created by the calling user. " + + "* shared : networks that have been granted to the calling user by another user. " + + "* all : all networks (account, domain and shared).") + private String networkFilter; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -166,6 +184,10 @@ public class ListNetworksCmd extends BaseListTaggedResourcesCmd implements UserC return networkOfferingId; } + public Long getAssociatedNetworkId() { + return associatedNetworkId; + } + @Override public Boolean getDisplay() { if (display != null) { @@ -178,6 +200,10 @@ public class ListNetworksCmd extends BaseListTaggedResourcesCmd implements UserC return showIcon != null ? showIcon : false; } + public String getNetworkFilter() { + return networkFilter; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/RemoveNetworkPermissionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/RemoveNetworkPermissionsCmd.java new file mode 100644 index 00000000000..5f6022fc736 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/RemoveNetworkPermissionsCmd.java @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.network; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.Network; +import com.cloud.user.Account; + +import java.util.List; + +@APICommand(name = RemoveNetworkPermissionsCmd.APINAME, description = "Removes network permissions.", + responseObject = SuccessResponse.class, + entityType = {Network.class}, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.17.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class RemoveNetworkPermissionsCmd extends BaseCmd { + public static final Logger LOGGER = Logger.getLogger(RemoveNetworkPermissionsCmd.class.getName()); + + public static final String APINAME = "removeNetworkPermissions"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ACCOUNTS, + type = CommandType.LIST, + collectionType = CommandType.STRING, + description = "a comma delimited list of accounts within owner's domain. If specified, \"op\" parameter has to be passed in.") + private List accountNames; + + @Parameter(name = ApiConstants.ACCOUNT_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = AccountResponse.class, + description = "a comma delimited list of account IDs within owner's domain. If specified, \"op\" parameter has to be passed in.") + private List accountIds; + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the network ID") + private Long networkId; + + @Parameter(name = ApiConstants.PROJECT_IDS, + type = CommandType.LIST, + collectionType = CommandType.UUID, + entityType = ProjectResponse.class, + description = "a comma delimited list of projects within owner's domain. If specified, \"op\" parameter has to be passed in.") + private List projectIds; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public List getAccountNames() { + return accountNames; + } + + public List getAccountIds() { + return accountIds; + } + + public Long getNetworkId() { + return networkId; + } + + public List getProjectIds() { + return projectIds; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public void execute() { + if (accountIds != null && accountNames != null) { + throw new InvalidParameterValueException("Accounts and accountNames can't be specified together"); + } + boolean result = _networkService.removeNetworkPermissions(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update network permissions"); + } + } + + @Override + public long getEntityOwnerId() { + Network network = _entityMgr.findById(Network.class, getNetworkId()); + if (network != null) { + return network.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/network/ResetNetworkPermissionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ResetNetworkPermissionsCmd.java new file mode 100644 index 00000000000..ac0ee1f8e28 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/network/ResetNetworkPermissionsCmd.java @@ -0,0 +1,89 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.network; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + +import com.cloud.network.Network; +import com.cloud.user.Account; + +@APICommand(name = ResetNetworkPermissionsCmd.APINAME, description = "Resets network permissions.", + responseObject = SuccessResponse.class, + entityType = {Network.class}, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.17.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ResetNetworkPermissionsCmd extends BaseCmd { + public static final Logger LOGGER = Logger.getLogger(ResetNetworkPermissionsCmd.class.getName()); + + public static final String APINAME = "resetNetworkPermissions"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, required = true, description = "the network ID") + private Long networkId; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public Long getNetworkId() { + return networkId; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public void execute() { + boolean result = _networkService.resetNetworkPermissions(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update network permissions"); + } + } + + @Override + public long getEntityOwnerId() { + Network network = _entityMgr.findById(Network.class, getNetworkId()); + if (network != null) { + return network.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } +} 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 dbcdb8f8884..59879562b02 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.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; @@ -124,6 +125,16 @@ public class RestartNetworkCmd extends BaseAsyncCmd { 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 8bff3ebfa54..5abd0492f16 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.RoleType; 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 class UpdateNetworkCmd extends BaseAsyncCustomIdCmd implements UserCmd { 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 fab1b598351..5e5e43cf609 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.ApiErrorCode; 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.exception.ResourceUnavailableException; 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 class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd { 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 class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd { return aclId; } - public Boolean getBypassVlanOverlapCheck() { - if (bypassVlanOverlapCheck != null) { - return bypassVlanOverlapCheck; - } - return false; + public Long getAssociatedNetworkId() { + return associatedNetworkId; } ///////////////////////////////////////////////////// @@ -157,9 +147,7 @@ public class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd { 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 class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd { @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 16ce223c08c..fab2a2cb4ce 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.ApiConstants; 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 00000000000..6bcc1d35cb1 --- /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/NetworkPermissionsResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkPermissionsResponse.java new file mode 100644 index 00000000000..fcecd14e282 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkPermissionsResponse.java @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import com.cloud.network.Network; +import com.cloud.serializer.Param; + +@EntityReference(value = Network.class) +@SuppressWarnings("unused") +public class NetworkPermissionsResponse extends BaseResponse { + @SerializedName(ApiConstants.NETWORK_ID) + @Param(description = "the network ID") + private String networkId; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "the ID of the domain to which the network belongs") + private String domainId; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "the name of the domain to which the network belongs") + private String domainName; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "the account the network is available for") + private String accountName; + + @SerializedName(ApiConstants.ACCOUNT_ID) + @Param(description = "the ID of account the network is available for") + private String accountId; + + @SerializedName(ApiConstants.PROJECT) + @Param(description = "the project the network is available for") + private String projectName; + + @SerializedName(ApiConstants.PROJECT_ID) + @Param(description = "the ID of project the network is available for") + private String projectId; + + + public void setNetworkId(String networkId) { + this.networkId = networkId; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } +} 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 b673788dbf5..79750a322cf 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 @@ -23,7 +23,7 @@ import java.util.Set; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseResponseWithAnnotations; +import org.apache.cloudstack.api.BaseResponseWithAssociatedNetwork; import org.apache.cloudstack.api.EntityReference; import com.cloud.network.Network; @@ -33,7 +33,7 @@ import com.google.gson.annotations.SerializedName; @SuppressWarnings("unused") @EntityReference(value = {Network.class, ProjectAccount.class}) -public class NetworkResponse extends BaseResponseWithAnnotations implements ControlledEntityResponse, SetResourceIconResponse { +public class NetworkResponse extends BaseResponseWithAssociatedNetwork implements ControlledEntityResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) @Param(description = "the id of the network") @@ -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) + @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; @@ -512,6 +520,14 @@ public class NetworkResponse extends BaseResponseWithAnnotations implements Cont 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/api/src/main/java/org/apache/cloudstack/api/response/PrivateGatewayResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/PrivateGatewayResponse.java index 381c7d16399..f5c0af5e150 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/PrivateGatewayResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/PrivateGatewayResponse.java @@ -19,7 +19,7 @@ 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 org.apache.cloudstack.api.BaseResponseWithAssociatedNetwork; import org.apache.cloudstack.api.EntityReference; import com.cloud.network.vpc.VpcGateway; @@ -27,7 +27,7 @@ import com.cloud.serializer.Param; @EntityReference(value = VpcGateway.class) @SuppressWarnings("unused") -public class PrivateGatewayResponse extends BaseResponse implements ControlledEntityResponse { +public class PrivateGatewayResponse extends BaseResponseWithAssociatedNetwork implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) @Param(description = "the id of the private gateway") @@ -190,5 +190,4 @@ public class PrivateGatewayResponse extends BaseResponse implements ControlledEn public void setAclName(String aclName) { this.aclName = aclName; } - } 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 1673575780c..9a08aebc8ab 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 @@ public interface NetworkOrchestrationService { */ 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 82713295a84..f422a013894 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 @@ -53,6 +53,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.network.dao.NetworkPermissionDao; import org.apache.commons.lang.BooleanUtils; import org.apache.log4j.Logger; @@ -318,6 +319,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra ResourceManager resourceManager; @Inject private AnnotationDao annotationDao; + @Inject + NetworkPermissionDao networkPermissionDao; List networkGurus; @@ -2530,7 +2533,9 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra 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 +2545,12 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra 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 +2773,18 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra 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. * @@ -3096,7 +3117,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra 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 { @@ -3118,6 +3139,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } networkDetailsDao.removeDetails(networkFinal.getId()); + networkPermissionDao.removeAllPermissions(networkFinal.getId()); } final NetworkOffering ntwkOff = _entityMgr.findById(NetworkOffering.class, networkFinal.getNetworkOfferingId()); @@ -3150,8 +3172,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra 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; @@ -3171,6 +3193,13 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra _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; } @@ -3206,6 +3235,11 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra 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 d3959df8af8..b2b12bba686 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.Table; 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 class DataCenterVnetVO implements InternalIdentity { return vnet; } + @Override public String getReservationId() { return reservationId; } @@ -92,6 +93,7 @@ public class DataCenterVnetVO implements InternalIdentity { this.reservationId = reservationId; } + @Override public Long getAccountId() { return accountId; } @@ -100,10 +102,12 @@ public class DataCenterVnetVO implements InternalIdentity { this.accountId = accountId; } + @Override public long getDataCenterId() { return dataCenterId; } + @Override public long getPhysicalNetworkId() { return physicalNetworkId; } @@ -112,6 +116,7 @@ public class DataCenterVnetVO implements InternalIdentity { 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 20b07e983b8..7423ded598f 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 class VlanVO implements Vlan { .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 4234c73441f..2826abf9cac 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.GenerationType; 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 338ef57513b..ff187687fdf 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 class NetworkOfferingVO implements NetworkOffering { 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 dc71f898970..85e75178869 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.annotation.PostConstruct; 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 class DomainRouterDaoImpl extends GenericDaoBase im 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/engine/schema/src/main/java/org/apache/cloudstack/network/NetworkPermissionVO.java b/engine/schema/src/main/java/org/apache/cloudstack/network/NetworkPermissionVO.java new file mode 100644 index 00000000000..fd75db9bbd7 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/NetworkPermissionVO.java @@ -0,0 +1,61 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.network; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.cloud.network.NetworkPermission; + +@Entity +@Table(name = "network_permissions") +public class NetworkPermissionVO implements NetworkPermission { + @Id + @Column(name = "id") + private Long id; + + @Column(name = "network_id") + private long networkId; + + @Column(name = "account_id") + private long accountId; + + public NetworkPermissionVO() { + } + + public NetworkPermissionVO(long networkId, long accountId) { + this.networkId = networkId; + this.accountId = accountId; + } + + @Override + public long getId() { + return id; + } + + @Override + public long getNetworkId() { + return networkId; + } + + @Override + public long getAccountId() { + return accountId; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/NetworkPermissionDao.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/NetworkPermissionDao.java new file mode 100644 index 00000000000..1c8d1cf48ff --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/NetworkPermissionDao.java @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.network.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.network.NetworkPermissionVO; + +public interface NetworkPermissionDao extends GenericDao { + /** + * remove the ability to Network vms from the given network for the given + * account names which are valid in the given domain + * + * @param networkId + * id of the network to modify Network permissions + * @param accountIds + * list of account ids + */ + void removePermissions(long networkId, List accountIds); + + /** + * remove all Network permissions associated with a network + * + * @param networkId + */ + void removeAllPermissions(long networkId); + + /** + * Find a Network permission by networkId, accountName, and domainId + * + * @param networkId + * the id of the network to search for + * @param accountId + * the id of the account for which permission is being searched + * @return Network permission if found, null otherwise + */ + NetworkPermissionVO findByNetworkAndAccount(long networkId, long accountId); + + /** + * List all Network permissions for the given network + * + * @param networkId + * id of the network for which Network permissions will be + * queried + * @return list of Network permissions + */ + List findByNetwork(long networkId); + + List listPermittedNetworkIdsByAccounts(List permittedAccounts); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/network/dao/NetworkPermissionDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/NetworkPermissionDaoImpl.java new file mode 100644 index 00000000000..ffc62b15dc2 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/network/dao/NetworkPermissionDaoImpl.java @@ -0,0 +1,98 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.network.dao; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import org.apache.cloudstack.network.NetworkPermissionVO; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Component +public class NetworkPermissionDaoImpl extends GenericDaoBase implements NetworkPermissionDao { + private static final Logger s_logger = Logger.getLogger(NetworkPermissionDaoImpl.class); + + private SearchBuilder NetworkAndAccountSearch; + private SearchBuilder NetworkIdSearch; + private GenericSearchBuilder FindNetworkIdsByAccount; + + protected NetworkPermissionDaoImpl() { + NetworkAndAccountSearch = createSearchBuilder(); + NetworkAndAccountSearch.and("networkId", NetworkAndAccountSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + NetworkAndAccountSearch.and("accountId", NetworkAndAccountSearch.entity().getAccountId(), SearchCriteria.Op.IN); + NetworkAndAccountSearch.done(); + + NetworkIdSearch = createSearchBuilder(); + NetworkIdSearch.and("networkId", NetworkIdSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + NetworkIdSearch.done(); + + FindNetworkIdsByAccount = createSearchBuilder(Long.class); + FindNetworkIdsByAccount.select(null, SearchCriteria.Func.DISTINCT, FindNetworkIdsByAccount.entity().getNetworkId()); + FindNetworkIdsByAccount.and("account", FindNetworkIdsByAccount.entity().getAccountId(), SearchCriteria.Op.IN); + FindNetworkIdsByAccount.done(); + } + + @Override + public void removePermissions(long networkId, List accountIds) { + if (accountIds.isEmpty()) { + return; + } + SearchCriteria sc = NetworkAndAccountSearch.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("accountId", accountIds.toArray()); + expunge(sc); + } + + @Override + public void removeAllPermissions(long networkId) { + SearchCriteria sc = NetworkIdSearch.create(); + sc.setParameters("networkId", networkId); + expunge(sc); + } + + @Override + public NetworkPermissionVO findByNetworkAndAccount(long networkId, long accountId) { + SearchCriteria sc = NetworkAndAccountSearch.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("accountId", accountId); + return findOneBy(sc); + } + + @Override + public List findByNetwork(long networkId) { + SearchCriteria sc = NetworkIdSearch.create(); + sc.setParameters("networkId", networkId); + return listBy(sc); + } + + @Override + public List listPermittedNetworkIdsByAccounts(List permittedAccounts) { + SearchCriteria sc = FindNetworkIdsByAccount.create(); + if (permittedAccounts != null && !permittedAccounts.isEmpty()) { + sc.setParameters("account", permittedAccounts.toArray()); + return customSearch(sc, null); + } + return new ArrayList(); + } +} diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 437b507cd60..2b0b56b4684 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -296,4 +296,5 @@ + diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql b/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql index 15a589da447..4c56a3b98b8 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql @@ -645,6 +645,15 @@ CREATE VIEW `cloud`.`domain_router_view` AS INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) SELECT UUID(), 3, 'listConfigurations', 'ALLOW', (SELECT MAX(`sort_order`)+1 FROM `cloud`.`role_permissions`) ON DUPLICATE KEY UPDATE rule=rule; INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) SELECT UUID(), 3, 'updateConfiguration', 'ALLOW', (SELECT MAX(`sort_order`)+1 FROM `cloud`.`role_permissions`) ON DUPLICATE KEY UPDATE rule=rule; +-- table for network permissions +CREATE TABLE `cloud`.`network_permissions` ( + `id` bigint unsigned NOT NULL auto_increment, + `network_id` bigint unsigned NOT NULL, + `account_id` bigint unsigned NOT NULL, + PRIMARY KEY (`id`), + INDEX `i_network_permission_network_id`(`network_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + INSERT INTO `cloud`.`user_vm_details`(`vm_id`, `name`, `value`) SELECT `user_vm_details`.`vm_id`, 'SSH.KeyPairNames', `ssh_keypairs`.`keypair_name` FROM `cloud`.`user_vm_details` diff --git a/server/src/main/java/com/cloud/acl/DomainChecker.java b/server/src/main/java/com/cloud/acl/DomainChecker.java index 387535dc19c..298cefb6bcd 100644 --- a/server/src/main/java/com/cloud/acl/DomainChecker.java +++ b/server/src/main/java/com/cloud/acl/DomainChecker.java @@ -41,6 +41,7 @@ import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.UnavailableCommandException; import com.cloud.network.Network; import com.cloud.network.NetworkModel; +import com.cloud.network.router.VirtualRouter; import com.cloud.network.vpc.VpcOffering; import com.cloud.network.vpc.dao.VpcOfferingDetailsDao; import com.cloud.offering.DiskOffering; @@ -173,6 +174,10 @@ public class DomainChecker extends AdapterBase implements SecurityChecker { 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 VirtualRouter) { + _networkMgr.checkRouterPermissions(caller, (VirtualRouter)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 e8ac1628c64..da325df9d71 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -46,6 +46,7 @@ import org.apache.cloudstack.annotation.dao.AnnotationDao; import org.apache.cloudstack.api.ApiConstants.DomainDetails; import org.apache.cloudstack.api.ApiConstants.HostDetails; import org.apache.cloudstack.api.ApiConstants.VMDetails; +import org.apache.cloudstack.api.BaseResponseWithAssociatedNetwork; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; @@ -82,6 +83,7 @@ import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse; 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; @@ -102,6 +104,7 @@ import org.apache.cloudstack.api.response.ManagementServerResponse; import org.apache.cloudstack.api.response.NetworkACLItemResponse; import org.apache.cloudstack.api.response.NetworkACLResponse; import org.apache.cloudstack.api.response.NetworkOfferingResponse; +import org.apache.cloudstack.api.response.NetworkPermissionsResponse; import org.apache.cloudstack.api.response.NetworkResponse; import org.apache.cloudstack.api.response.NicExtraDhcpOptionResponse; import org.apache.cloudstack.api.response.NicResponse; @@ -245,12 +248,14 @@ import com.cloud.host.Host; 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; import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkPermission; import com.cloud.network.NetworkProfile; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.IsolationType; @@ -277,6 +282,7 @@ import com.cloud.network.as.Counter; 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; @@ -423,6 +429,8 @@ public class ApiResponseHelper implements ResponseGenerator { private AnnotationDao annotationDao; @Inject private UserStatisticsDao userStatsDao; + @Inject + private NetworkDao networkDao; @Override public UserResponse createUserResponse(User user) { @@ -2437,6 +2445,9 @@ public class ApiResponseHelper implements ResponseGenerator { response.setVpcName(vpc.getName()); } } + + setResponseAssociatedNetworkInformation(response, network.getId()); + response.setCanUseForDeploy(ApiDBUtils.canUseForDeploy(network)); // set tag information @@ -2491,6 +2502,18 @@ public class ApiResponseHelper implements ResponseGenerator { return response; } + private void setResponseAssociatedNetworkInformation(BaseResponseWithAssociatedNetwork response, Long networkId) { + final NetworkDetailVO detail = networkDetailsDao.findDetail(networkId, 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()); + } + } + } + @Override public Long getSecurityGroupId(String groupName, long accountId) { SecurityGroup sg = ApiDBUtils.getSecurityGroup(groupName, accountId); @@ -2762,7 +2785,7 @@ public class ApiResponseHelper implements ResponseGenerator { } @Override - public GuestVlanRangeResponse createDedicatedGuestVlanRangeResponse(GuestVlan vlan) { + public GuestVlanRangeResponse createDedicatedGuestVlanRangeResponse(GuestVlanRange vlan) { GuestVlanRangeResponse guestVlanRangeResponse = new GuestVlanRangeResponse(); guestVlanRangeResponse.setId(vlan.getUuid()); @@ -3202,6 +3225,8 @@ public class ApiResponseHelper implements ResponseGenerator { response.setAclName(acl.getName()); } + setResponseAssociatedNetworkInformation(response, result.getNetworkId()); + response.setObjectName("privategateway"); return response; @@ -4558,6 +4583,95 @@ public class ApiResponseHelper implements ResponseGenerator { 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; + } + + @Override + public NetworkPermissionsResponse createNetworkPermissionsResponse(NetworkPermission permission) { + Long networkOwnerDomain = null; + Network network = ApiDBUtils.findNetworkById(permission.getNetworkId()); + + NetworkPermissionsResponse response = new NetworkPermissionsResponse(); + response.setNetworkId(network.getUuid()); + + Account networkOwner = ApiDBUtils.findAccountById(network.getAccountId()); + if (networkOwner != null) { + networkOwnerDomain = networkOwner.getDomainId(); + if (networkOwnerDomain != null) { + Domain domain = ApiDBUtils.findDomainById(networkOwnerDomain); + if (domain != null) { + response.setDomainId(domain.getUuid()); + response.setDomainName(domain.getName()); + } + } + } + + Account account = ApiDBUtils.findAccountById(permission.getAccountId()); + response.setAccountName(account.getName()); + response.setAccountId(account.getUuid()); + if (account.getType() == Account.Type.PROJECT) { + // convert account to projectIds + Project project = ApiDBUtils.findProjectByProjectAccountId(account.getId()); + + if (project.getUuid() != null && !project.getUuid().isEmpty()) { + response.setProjectId(project.getUuid()); + } else { + response.setProjectId(String.valueOf(project.getId())); + } + response.setProjectName(project.getName()); + } + + response.setObjectName("networkpermission"); + return response; + } + protected void handleCertificateResponse(String certStr, DirectDownloadCertificateResponse response) { try { Certificate cert = CertificateHelper.buildCertificate(certStr); 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 e2db9f29b20..45c1d7cb595 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; 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; @@ -291,7 +293,14 @@ public class ParamProcessWorker implements DispatchWorker { 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 24d64a9fca3..5fbfeeb658d 100755 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -116,6 +116,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; 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; @@ -4494,6 +4495,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } + boolean isSharedNetworkWithoutSpecifyVlan = _networkMgr.isSharedNetworkWithoutSpecifyVlan(_networkOfferingDao.findById(network.getNetworkOfferingId())); if (ipv4) { final String newCidr = NetUtils.getCidrFromGatewayAndNetmask(vlanGateway, vlanNetmask); @@ -4518,69 +4520,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati 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); } } @@ -4611,6 +4552,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } // 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()); @@ -4632,6 +4576,68 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati 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) { @@ -5025,7 +5031,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @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)); @@ -6037,14 +6043,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati 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 052b1e003b3..66d79cc3504 100644 --- a/server/src/main/java/com/cloud/network/NetworkModelImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkModelImpl.java @@ -41,6 +41,8 @@ import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao; +import org.apache.cloudstack.network.NetworkPermissionVO; +import org.apache.cloudstack.network.dao.NetworkPermissionDao; import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; @@ -74,6 +76,8 @@ import com.cloud.network.addr.PublicIp; 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; @@ -91,6 +95,7 @@ import com.cloud.network.element.IpDeployer; import com.cloud.network.element.IpDeployingRequester; import com.cloud.network.element.NetworkElement; import com.cloud.network.element.UserDataServiceProvider; +import com.cloud.network.router.VirtualRouter; import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.network.rules.dao.PortForwardingRulesDao; @@ -168,6 +173,8 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi VpcGatewayDao _vpcGatewayDao; @Inject ProjectDao projectDao; + @Inject + NetworkPermissionDao _networkPermissionDao; private List networkElements; @@ -179,6 +186,8 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi this.networkElements = networkElements; } + @Inject + NetworkAccountDao _networkAccountDao; @Inject NetworkDomainDao _networkDomainDao; @Inject @@ -217,7 +226,6 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi private NetworkService _networkService; private final HashMap _systemNetworks = new HashMap(5); - static Long s_privateOfferingId = null; SearchBuilder IpAddressSearch; SearchBuilder NicForTrafficTypeSearch; @@ -1665,26 +1673,11 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO)network).getUuid() + ", network does not have an owner"); if (owner.getType() != Account.Type.PROJECT && networkOwner.getType() == 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()) { + NetworkPermissionVO networkPermission = _networkPermissionDao.findByNetworkAndAccount(network.getId(), owner.getId()); + if (CollectionUtils.isEmpty(networkMap) && networkPermission == null) { throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO)network).getUuid() + ", permission denied"); } @@ -1702,6 +1695,131 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi } } + 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 (owner.getType() == Account.Type.ADMIN) { + return; + } + if (network.getGuestType() == GuestType.Shared) { + checkSharedNetworkOperatePermissions(owner, network); + } else { + checkNonSharedNetworkOperatePermissions(owner, network); + } + } + + @Override + public void checkRouterPermissions(Account owner, VirtualRouter router) { + Account account = _accountMgr.getAccount(router.getAccountId()); + try { + _accountMgr.checkAccess(owner, null, true, account); + return; + } catch (PermissionDeniedException ex) { + s_logger.info("Account " + owner + " do not have permission on router owner " + account); + } + List routerNics = _nicDao.listByVmId(router.getId()); + for (final Nic routerNic : routerNics) { + final NetworkVO network = _networksDao.findById(routerNic.getNetworkId()); + if (TrafficType.Guest.equals(network.getTrafficType())) { + checkNetworkOperatePermissions(owner, network); + } + } + } + + private void checkNonSharedNetworkOperatePermissions(Account owner, Network network) { + // check on isolated/L2 networks + Account networkOwner = _accountDao.findByIdIncludingRemoved(network.getAccountId()); + if (owner.getType() == 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.Type.NORMAL) { + if (owner.getType() != Account.Type.PROJECT && networkOwner.getType() == 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.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.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.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.Type.PROJECT && networkOwner.getType() == 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.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 { @@ -2089,10 +2207,12 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi 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 a4675792d48..9b747650908 100644 --- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.network; +import org.apache.commons.lang3.EnumUtils; + import java.net.Inet6Address; import java.net.InetAddress; import java.net.URI; @@ -25,6 +27,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -35,6 +38,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; @@ -46,10 +50,16 @@ import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin; 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; +import org.apache.cloudstack.api.command.user.network.CreateNetworkPermissionsCmd; +import org.apache.cloudstack.api.command.user.network.ListNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; +import org.apache.cloudstack.api.command.user.network.RemoveNetworkPermissionsCmd; +import org.apache.cloudstack.api.command.user.network.ResetNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd; import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd; import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; @@ -61,6 +71,8 @@ import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; +import org.apache.cloudstack.network.NetworkPermissionVO; +import org.apache.cloudstack.network.dao.NetworkPermissionDao; import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -139,6 +151,7 @@ import com.cloud.network.element.NetworkElement; 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; @@ -181,6 +194,7 @@ import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; import com.cloud.utils.db.EntityManager; import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchBuilder; @@ -259,6 +273,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C @Inject NetworkDao _networksDao = null; @Inject + NetworkPermissionDao _networkPermissionDao = null; + @Inject NicDao _nicDao = null; @Inject RulesManager _rulesMgr; @@ -306,7 +322,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C @Inject InternalLoadBalancerElementService _internalLbElementSvc; @Inject - DataCenterVnetDao _datacneterVnet; + DataCenterVnetDao _dcVnetDao; @Inject AccountGuestVlanMapDao _accountGuestVlanMapDao; @Inject @@ -1228,6 +1244,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C String isolatedPvlan = cmd.getIsolatedPvlan(); String externalId = cmd.getExternalId(); String isolatedPvlanType = cmd.getIsolatedPvlanType(); + Long associatedNetworkId = cmd.getAssociatedNetworkId(); // Validate network offering NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId); @@ -1302,15 +1319,16 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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"); } @@ -1451,12 +1469,16 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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); @@ -1473,6 +1495,11 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C validateNetworkOfferingForNonRootAdminUser(ntwkOff); } + // 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 if (!_accountMgr.isRootAdmin(caller.getId()) && (ntwkOff.isSpecifyVlan() || vlanId != null || bypassVlanOverlapCheck)) { throw new InvalidParameterValueException("Only ROOT admin is allowed to specify vlanId or bypass vlan overlap check"); @@ -1531,9 +1558,23 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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)); @@ -1541,27 +1582,72 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C // 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); - } - 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 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 && associatedNetwork.getGuestType() != GuestType.L2) { + throw new InvalidParameterValueException("Associated network MUST be an Isolated or L2 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 && associatedNetwork.getCidr() != 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()); + } + } + } + associatedNetwork = implementedNetworkInCreation(caller, zone, associatedNetwork); + if (associatedNetwork == null || (associatedNetwork.getState() != Network.State.Implemented && associatedNetwork.getState() != Network.State.Setup)) { + 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; + } + } + private void validateNetworkOfferingForNonRootAdminUser(NetworkOfferingVO ntwkOff) { if (ntwkOff.getTrafficType() != TrafficType.Guest) { throw new InvalidParameterValueException("This user can only create a Guest network"); @@ -1569,9 +1655,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C if (ntwkOff.getGuestType() == GuestType.L2 || ntwkOff.getGuestType() == GuestType.Isolated) { s_logger.debug(String.format("Creating a network from network offerings having traffic type [%s] and network type [%s].", TrafficType.Guest, ntwkOff.getGuestType())); + } 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("This user can only create an %s network or a %s network.", GuestType.Isolated, GuestType.L2)); + String.format("This user can only create an %s network, a %s network or a %s network with specifyVlan=false.", GuestType.Isolated, GuestType.L2, GuestType.Shared)); } } @@ -1618,11 +1707,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C } } - private Network commitNetwork(final Long networkOfferingId, final String gateway, final String startIP, final String endIP, final String netmask, final String networkDomain, final String 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 @@ -1645,6 +1735,20 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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) { @@ -1680,11 +1784,14 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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; } }); @@ -1733,6 +1840,13 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C Boolean forVpc = cmd.getForVpc(); Boolean display = cmd.getDisplay(); Long networkOfferingId = cmd.getNetworkOfferingId(); + Long associatedNetworkId = cmd.getAssociatedNetworkId(); + String networkFilterStr = cmd.getNetworkFilter(); + + 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 @@ -1740,6 +1854,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C isSystem = false; } + // check network filter + if (networkFilterStr != null && !EnumUtils.isValidEnumIgnoreCase(Network.NetworkFilter.class, networkFilterStr)) { + throw new InvalidParameterValueException("Invalid value of networkfilter: " + networkFilterStr); + } + Network.NetworkFilter networkFilter = networkFilterStr != null ? EnumUtils.getEnumIgnoreCase(Network.NetworkFilter.class, networkFilterStr) : Network.NetworkFilter.All; + // Account/domainId parameters and isSystem are mutually exclusive if (isSystem != null && isSystem && (accountName != null || domainId != null)) { throw new InvalidParameterValueException("System network belongs to system, account and domainId parameters can't be specified"); @@ -1856,34 +1976,60 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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)); - //get domain level networks - if (domainId != null) { + if (Arrays.asList(Network.NetworkFilter.Account, Network.NetworkFilter.AccountDomain, Network.NetworkFilter.All).contains(networkFilter)) { + //get account level networks + networksToReturn.addAll(listAccountSpecificNetworks(buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, networkOfferingId, + aclType, skipProjectNetworks, restartRequired, specifyIpRanges, vpcId, tags, display, vlanId, associatedNetworkId), searchFilter, permittedAccounts)); + } + if (domainId != null && Arrays.asList(Network.NetworkFilter.Domain, Network.NetworkFilter.AccountDomain, Network.NetworkFilter.All).contains(networkFilter)) { + //get domain level networks 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)); + } + if (Arrays.asList(Network.NetworkFilter.Shared, Network.NetworkFilter.All).contains(networkFilter)) { + // get shared networks + List sharedNetworks = listSharedNetworks(buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, networkOfferingId, + aclType, skipProjectNetworks, restartRequired, specifyIpRanges, vpcId, tags, display, vlanId, associatedNetworkId), searchFilter, permittedAccounts); + addNetworksToReturnIfNotExist(networksToReturn, sharedNetworks); + } } 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)); - //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)); - //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)); + if (Arrays.asList(Network.NetworkFilter.Account, Network.NetworkFilter.AccountDomain, Network.NetworkFilter.All).contains(networkFilter)) { + //add account specific networks + networksToReturn.addAll(listAccountSpecificNetworksByDomainPath(buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, networkOfferingId, + aclType, skipProjectNetworks, restartRequired, specifyIpRanges, vpcId, tags, display, vlanId, associatedNetworkId), searchFilter, path, isRecursive)); + } + if (Arrays.asList(Network.NetworkFilter.Domain, Network.NetworkFilter.AccountDomain, Network.NetworkFilter.All).contains(networkFilter)) { + //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, 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, vlanId, associatedNetworkId), searchFilter, caller.getDomainId(), true)); + } + } + if (Arrays.asList(Network.NetworkFilter.Shared, Network.NetworkFilter.All).contains(networkFilter)) { + // get shared networks + List sharedNetworks = listSharedNetworksByDomainPath(buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, networkOfferingId, + aclType, skipProjectNetworks, restartRequired, specifyIpRanges, vpcId, tags, display, vlanId, associatedNetworkId), searchFilter, path, isRecursive); + addNetworksToReturnIfNotExist(networksToReturn, sharedNetworks); } } } 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()) { @@ -1930,10 +2076,20 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C return new Pair, Integer>(networksToReturn, networksToReturn.size()); } + private void addNetworksToReturnIfNotExist(final List networksToReturn, final List sharedNetworks) { + Set networkIds = networksToReturn.stream() + .map(NetworkVO::getId) + .collect(Collectors.toSet()); + List sharedNetworksToReturn = sharedNetworks.stream() + .filter(network -> ! networkIds.contains(network.getId())) + .collect(Collectors.toList()); + networksToReturn.addAll(sharedNetworksToReturn); + } + 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(); @@ -2007,6 +2163,17 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C sc.addAnd("networkOfferingId", SearchCriteria.Op.EQ, networkOfferingId); } + if (associatedNetworkId != null) { + sc.setJoinParameters("associatedNetworkSearch", "name", Network.AssociatedNetworkId); + sc.setJoinParameters("associatedNetworkSearch", "value", String.valueOf(associatedNetworkId)); + } + + if (vlanId != null) { + SearchCriteria 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; } @@ -2097,6 +2264,49 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C } } + private List listSharedNetworks(SearchCriteria sc, Filter searchFilter, List permittedAccounts) { + List sharedNetworkIds = _networkPermissionDao.listPermittedNetworkIdsByAccounts(permittedAccounts); + if (!sharedNetworkIds.isEmpty()) { + SearchCriteria ssc = _networksDao.createSearchCriteria(); + ssc.addAnd("id", SearchCriteria.Op.IN, sharedNetworkIds.toArray()); + sc.addAnd("id", SearchCriteria.Op.SC, ssc); + return _networksDao.search(sc, searchFilter); + } + return new ArrayList(); + } + + private List listSharedNetworksByDomainPath(SearchCriteria sc, Filter searchFilter, String path, boolean isRecursive) { + Set allowedDomains = new HashSet(); + if (path != null) { + if (isRecursive) { + allowedDomains = _domainMgr.getDomainChildrenIds(path); + } else { + Domain domain = _domainDao.findDomainByPath(path); + allowedDomains.add(domain.getId()); + } + } + List allowedDomainsList = new ArrayList(allowedDomains); + + if (!allowedDomainsList.isEmpty()) { + GenericSearchBuilder accountIdSearch = _accountDao.createSearchBuilder(Long.class); + accountIdSearch.and("domainId", accountIdSearch.entity().getDomainId(), SearchCriteria.Op.IN); + accountIdSearch.selectFields(accountIdSearch.entity().getId()); + accountIdSearch.done(); + SearchCriteria scAccount = accountIdSearch.create(); + scAccount.setParameters("domainId", allowedDomainsList.toArray()); + List allowedAccountsList = _accountDao.customSearch(scAccount, null); + + List sharedNetworkIds = _networkPermissionDao.listPermittedNetworkIdsByAccounts(allowedAccountsList); + if (!sharedNetworkIds.isEmpty()) { + SearchCriteria ssc = _networksDao.createSearchCriteria(); + ssc.addAnd("id", SearchCriteria.Op.IN, sharedNetworkIds.toArray()); + sc.addAnd("id", SearchCriteria.Op.SC, ssc); + return _networksDao.search(sc, searchFilter); + } + } + return new ArrayList(); + } + @Override @ActionEvent(eventType = EventTypes.EVENT_NETWORK_DELETE, eventDescription = "deleting network", async = true) public boolean deleteNetwork(long networkId, boolean forced) { @@ -2111,15 +2321,17 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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"); @@ -2164,7 +2376,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C } Account callerAccount = _accountMgr.getActiveAccountById(user.getAccountId()); - _accountMgr.checkAccess(callerAccount, null, true, network); + _accountMgr.checkAccess(callerAccount, AccessType.OperateEntry, true, network); if (!network.isRedundant() && makeRedundant) { network.setRedundant(true); if (!_networksDao.update(network.getId(), network)) { @@ -2356,7 +2568,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C throw new InvalidParameterValueException("Can't allow networks which traffic type is not " + TrafficType.Guest); } - _accountMgr.checkAccess(callerAccount, null, true, network); + _accountMgr.checkAccess(callerAccount, AccessType.OperateEntry, true, network); _accountMgr.checkAccess(_accountMgr.getActiveAccountById(network.getAccountId()), offering, _dcDao.findById(network.getDataCenterId())); if (cmd instanceof UpdateNetworkCmdByAdmin) { @@ -3367,7 +3579,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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++) { @@ -3409,7 +3621,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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); } @@ -3445,7 +3657,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C // 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()) { @@ -3517,13 +3729,13 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C int i; 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"); } @@ -3546,8 +3758,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C String[] range = vnet.split("-"); Integer start = Integer.parseInt(range[0]); Integer end = Integer.parseInt(range[1]); - _datacneterVnet.lockRange(network.getDataCenterId(), network.getId(), start, end); - 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); @@ -3696,7 +3908,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C @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(); @@ -3771,7 +3983,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C // 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()) { @@ -3821,7 +4033,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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; @@ -3845,9 +4057,9 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C // 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; } @@ -3869,7 +4081,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C } @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(); @@ -3942,7 +4154,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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 @@ -3956,7 +4168,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C } // Remove dedication for the guest vlan - _datacneterVnet.releaseDedicatedGuestVlans(dedicatedGuestVlan.getId()); + _dcVnetDao.releaseDedicatedGuestVlans(dedicatedGuestVlan.getId()); if (_accountGuestVlanMapDao.remove(dedicatedGuestVlanRangeId)) { return true; } else { @@ -4646,9 +4858,10 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C @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 @@ -4686,13 +4899,22 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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; @@ -4710,6 +4932,9 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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"); } @@ -4858,6 +5083,210 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C 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, "%" + keyword + "%"); + } + 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 List listNetworkPermissions(ListNetworkPermissionsCmd cmd) { + final Long networkId = cmd.getNetworkId(); + NetworkVO network = _networksDao.findById(networkId); + if (network == null) { + throw new InvalidParameterValueException("unable to find network with id " + networkId); + } + final Account caller = CallContext.current().getCallingAccount(); + _accountMgr.checkAccess(caller, AccessType.OperateEntry, true, network); + + List accountNames = new ArrayList(); + List permissions = _networkPermissionDao.findByNetwork(networkId); + return permissions; + } + + @Override + public boolean createNetworkPermissions(CreateNetworkPermissionsCmd cmd) { + final Long id = cmd.getNetworkId(); + List accountNames = cmd.getAccountNames(); + List accountIds = cmd.getAccountIds(); + List projectIds = cmd.getProjectIds(); + + final Account caller = CallContext.current().getCallingAccount(); + NetworkVO network = validateNetworkPermissionParameters(caller, id); + + accountIds = populateAccounts(caller, accountIds, network.getDomainId(), accountNames, projectIds); + + final List accountIdsFinal = accountIds; + final Account owner = _accountMgr.getAccount(network.getAccountId()); + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + for (Long accountId : accountIdsFinal) { + Account permittedAccount = _accountDao.findActiveAccountById(accountId, network.getDomainId()); + if (permittedAccount != null) { + if (permittedAccount.getId() == owner.getId()) { + continue; // don't grant permission to the network owner, they implicitly have permission + } + NetworkPermissionVO existingPermission = _networkPermissionDao.findByNetworkAndAccount(id, permittedAccount.getId()); + if (existingPermission == null) { + NetworkPermissionVO networkPermission = new NetworkPermissionVO(id, permittedAccount.getId()); + _networkPermissionDao.persist(networkPermission); + } + } else { + throw new InvalidParameterValueException("Unable to find account " + accountId + " in the domain of network " + network + ". No permissions is added"); + } + } + } + }); + + return true; + } + + @Override + public boolean removeNetworkPermissions(RemoveNetworkPermissionsCmd cmd) { + final Long id = cmd.getNetworkId(); + List accountNames = cmd.getAccountNames(); + List accountIds = cmd.getAccountIds(); + List projectIds = cmd.getProjectIds(); + + final Account caller = CallContext.current().getCallingAccount(); + NetworkVO network = validateNetworkPermissionParameters(caller, id); + + accountIds = populateAccounts(caller, accountIds, network.getDomainId(), accountNames, projectIds); + + _networkPermissionDao.removePermissions(id, accountIds); + + return true; + } + + @Override + public boolean resetNetworkPermissions(ResetNetworkPermissionsCmd cmd) { + + final Long id = cmd.getNetworkId(); + + final Account caller = CallContext.current().getCallingAccount(); + NetworkVO network = validateNetworkPermissionParameters(caller, id); + + _networkPermissionDao.removeAllPermissions(id); + + return true; + } + + private NetworkVO validateNetworkPermissionParameters(Account caller, Long id) { + + final NetworkVO network = _networksDao.findById(id); + + if (network == null) { + throw new InvalidParameterValueException("unable to find network with id " + id); + } + + if (network.getAclType() == ACLType.Domain) { + throw new InvalidParameterValueException("network is already shared in domain"); + } + + if (network.getVpcId() != null) { + throw new InvalidParameterValueException("VPC tiers cannot be shared"); + } + + _accountMgr.checkAccess(caller, AccessType.OperateEntry, true, network); + + final Account owner = _accountMgr.getAccount(network.getAccountId()); + if (owner.getType() == Account.Type.PROJECT) { + // Currently project owned networks cannot be shared outside project but is available to all users within project by default. + throw new InvalidParameterValueException("Update network permissions is an invalid operation on network " + network.getName() + + ". Project owned networks cannot be shared outside network."); + } + + //Only admin or owner of the network should be able to change its permissions + if (caller.getId() != owner.getId() && !_accountMgr.isAdmin(caller.getId())) { + throw new InvalidParameterValueException("Unable to grant permission to account " + caller.getAccountName() + " as it is neither admin nor owner or the network"); + } + + return network; + } + + private List populateAccounts(Account caller, List accountIds, Long domainId, List accountNames, List projectIds) { + if (accountIds == null) { + accountIds = new ArrayList(); + } + // convert projectIds to accountIds + if (projectIds != null) { + accountIds.addAll(convertProjectIdsToAccountIds(caller, projectIds)); + } + // convert accountNames to accountIds + if (accountNames != null) { + accountIds.addAll(convertAccountNamesToAccountIds(caller, domainId, accountNames)); + } + final Domain domain = _domainDao.findById(domainId); + for (Long accountId : accountIds) { + Account permittedAccount = _accountDao.findActiveAccountById(accountId, domain.getId()); + if (permittedAccount == null) { + throw new InvalidParameterValueException("Unable to find account " + accountId + " in domain id=" + domain.getUuid() + ". No permissions is removed"); + } + } + return accountIds; + } + + private List convertProjectIdsToAccountIds(final Account caller, final List projectIds) { + List accountIds = new ArrayList(); + for (Long projectId : projectIds) { + Project project = _projectMgr.getProject(projectId); + if (project == null) { + throw new InvalidParameterValueException("Unable to find project by id " + projectId); + } + + if (!_projectMgr.canAccessProjectAccount(caller, project.getProjectAccountId())) { + throw new InvalidParameterValueException("Account " + caller + " can't access project id=" + projectId); + } + accountIds.add(project.getProjectAccountId()); + } + return accountIds; + } + + private List convertAccountNamesToAccountIds(final Account caller, final Long domainId, final List accountNames) { + List accountIds = new ArrayList(); + for (String accountName : accountNames) { + Account permittedAccount = _accountDao.findActiveAccount(accountName, domainId); + if (permittedAccount == null) { + throw new InvalidParameterValueException("Unable to find account by name " + accountName); + } + if (permittedAccount.getId() != caller.getId()) { + accountIds.add(permittedAccount.getId()); + } + } + return accountIds; + } + @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 c1ce99c0606..a29afa775e9 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 3b3dabe1da6..6e2f1db5f27 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 class PrivateNetworkGuru extends AdapterBase implements NetworkGuru { 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/lb/LoadBalancingRulesManagerImpl.java b/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index d7dcb915206..087de40cd09 100644 --- a/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -31,6 +31,7 @@ import javax.inject.Inject; import com.cloud.offerings.NetworkOfferingServiceMapVO; import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; +import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.user.loadbalancer.CreateLBHealthCheckPolicyCmd; import org.apache.cloudstack.api.command.user.loadbalancer.CreateLBStickinessPolicyCmd; @@ -1011,9 +1012,9 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements _rulesMgr.checkRuleAndUserVm(loadBalancer, vm, caller); - if (vm.getAccountId() != loadBalancer.getAccountId()) { - throw new PermissionDeniedException("Cannot add virtual machines that do not belong to the same owner."); - } + Account vmOwner = _accountDao.findById(vm.getAccountId()); + Network network = _networkDao.findById(loadBalancer.getNetworkId()); + _accountMgr.checkAccess(vmOwner, SecurityChecker.AccessType.UseEntry, false, network); // Let's check to make sure the vm has a nic in the same network as // the load balancing rule. diff --git a/server/src/main/java/com/cloud/network/rules/RulesManagerImpl.java b/server/src/main/java/com/cloud/network/rules/RulesManagerImpl.java index c16eeb61c21..0feb240743f 100644 --- a/server/src/main/java/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/main/java/com/cloud/network/rules/RulesManagerImpl.java @@ -31,6 +31,7 @@ import com.cloud.storage.dao.VMTemplateDao; import com.cloud.vm.NicProfile; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VirtualMachineProfileImpl; +import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.api.command.user.firewall.ListPortForwardingRulesCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; @@ -170,13 +171,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules } } - _accountMgr.checkAccess(caller, null, true, ipAddress, userVm); - - // validate that IP address and userVM belong to the same account - if (ipAddress.getAllocatedToAccountId().longValue() != userVm.getAccountId()) { - throw new InvalidParameterValueException("Unable to create ip forwarding rule, IP address " + ipAddress + - " owner is not the same as owner of virtual machine " + userVm.toString()); - } + _accountMgr.checkAccess(caller, null, false, ipAddress, userVm); // validate that userVM is in the same availability zone as the IP address if (ipAddress.getDataCenterId() != userVm.getDataCenterId()) { @@ -195,16 +190,11 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules return; } - _accountMgr.checkAccess(caller, null, true, rule, userVm); + _accountMgr.checkAccess(caller, null, false, rule, userVm); if (userVm.getState() == VirtualMachine.State.Destroyed || userVm.getState() == VirtualMachine.State.Expunging) { throw new InvalidParameterValueException("Invalid user vm: " + userVm.getId()); } - - // This same owner check is actually not needed, since multiple entities OperateEntry trick guarantee that - if (rule.getAccountId() != userVm.getAccountId()) { - throw new InvalidParameterValueException("New rule " + rule + " and vm id=" + userVm.getId() + " belong to different accounts"); - } } @Override @@ -571,6 +561,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules } else { checkIpAndUserVm(ipAddress, vm, caller, false); } + Account vmOwner = _accountMgr.getAccount(vm.getAccountId()); + _accountMgr.checkAccess(vmOwner, SecurityChecker.AccessType.UseEntry, false, network); //is static nat is for vm secondary ip //dstIp = guestNic.getIp4Address(); 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 8aabcc0b405..7c86a887481 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.acl.ControlledEntity.ACLType; 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.vpc.dao.VpcServiceMapDao; 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 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @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,105 +1860,91 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis throw ex; } - PhysicalNetwork physNet = null; - // Validate physical network - if (physicalNetworkId == null) { - final List pNtwks = _ntwkModel.getPhysicalNtwksSupportingTrafficType(vpc.getZoneId(), TrafficType.Guest); - if (pNtwks.isEmpty() || pNtwks.size() != 1) { - throw new InvalidParameterValueException("Physical network can't be determined; pass physical network id"); - } - physNet = pNtwks.get(0); - physicalNetworkId = physNet.getId(); - } + NetworkOfferingVO ntwkOff = getVpcPrivateGatewayNetworkOffering(networkOfferingIdPassed, broadcastUri); + final Long networkOfferingId = ntwkOff.getId(); - if (physNet == null) { - physNet = _entityMgr.findById(PhysicalNetwork.class, physicalNetworkId); - } - final Long dcId = physNet.getDataCenterId(); + validateVpcPrivateGatewayAssociateNetworkId(ntwkOff, broadcastUri, associatedNetworkId, bypassVlanOverlapCheck); + + final Long dcId = vpc.getZoneId(); + physicalNetworkId = validateVpcPrivateGatewayPhysicalNetworkId(dcId, physicalNetworkId, associatedNetworkId, ntwkOff); + PhysicalNetwork physNet = _entityMgr.findById(PhysicalNetwork.class, physicalNetworkId);; 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); + 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()); - } - - final Long mac = dc.getMacAddress(); - final Long nextMac = mac + 1; - dc.setMacAddress(nextMac); - - 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); - - _dcDao.update(dc.getId(), dc); - } - - 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"); - } - - networkAclId = aclId; - } - - { // 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); - } - - // 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); - - s_logger.debug("Created vpc gateway entry " + gatewayVO); - - return gatewayVO; + // 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.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); + + _dcDao.update(dc.getId(), dc); + } + + 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"); + } + + networkAclId = aclId; + } + + { // 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); + } + + // 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); + + s_logger.debug("Created vpc gateway entry " + gatewayVO); } catch (final Exception e) { ExceptionUtil.rethrowRuntime(e); ExceptionUtil.rethrow(e, InsufficientCapacityException.class); @@ -1943,6 +1956,75 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return getVpcPrivateGateway(gatewayVO.getId()); } + private void validateVpcPrivateGatewayAssociateNetworkId(NetworkOfferingVO ntwkOff, String broadcastUri, Long associatedNetworkId, Boolean bypassVlanOverlapCheck) { + // 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"); + } + 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"); + } + } + + private NetworkOfferingVO getVpcPrivateGatewayNetworkOffering(Long networkOfferingIdPassed, String broadcastUri) { + // 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); + } + return ntwkOff; + } + + private Long validateVpcPrivateGatewayPhysicalNetworkId(Long dcId, Long physicalNetworkId, Long associatedNetworkId, NetworkOfferingVO ntwkOff) { + // 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(dcId, TrafficType.Guest); + if (pNtwks.isEmpty() || pNtwks.size() != 1) { + throw new InvalidParameterValueException("Physical network can't be determined; pass physical network id"); + } + physicalNetworkId = pNtwks.get(0).getId(); + } + return physicalNetworkId; + } + @Override @ActionEvent(eventType = EventTypes.EVENT_PRIVATE_GATEWAY_CREATE, eventDescription = "Applying VPC private gateway", async = true) public PrivateGateway applyVpcPrivateGateway(final long gatewayId, final boolean destroyOnFailure) throws ConcurrentOperationException, ResourceUnavailableException { @@ -2005,6 +2087,17 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis 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 b28e3de0559..cbf20d405da 100644 --- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java @@ -966,8 +966,10 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio 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 eae1dda4209..f2e70e67b91 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -44,6 +44,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.affinity.AffinityGroupProcessor; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; import org.apache.cloudstack.annotation.AnnotationService; @@ -134,6 +135,7 @@ import org.apache.cloudstack.api.command.admin.network.DeleteNetworkServiceProvi 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 +298,7 @@ import org.apache.cloudstack.api.command.admin.volume.RecoverVolumeCmdByAdmin; 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; @@ -408,15 +410,19 @@ import org.apache.cloudstack.api.command.user.nat.ListIpForwardingRulesCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkACLCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkACLListCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; +import org.apache.cloudstack.api.command.user.network.CreateNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.DeleteNetworkACLCmd; import org.apache.cloudstack.api.command.user.network.DeleteNetworkACLListCmd; import org.apache.cloudstack.api.command.user.network.DeleteNetworkCmd; import org.apache.cloudstack.api.command.user.network.ListNetworkACLListsCmd; import org.apache.cloudstack.api.command.user.network.ListNetworkACLsCmd; import org.apache.cloudstack.api.command.user.network.ListNetworkOfferingsCmd; +import org.apache.cloudstack.api.command.user.network.ListNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; import org.apache.cloudstack.api.command.user.network.MoveNetworkAclItemCmd; +import org.apache.cloudstack.api.command.user.network.RemoveNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.ReplaceNetworkACLListCmd; +import org.apache.cloudstack.api.command.user.network.ResetNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd; import org.apache.cloudstack.api.command.user.network.UpdateNetworkACLItemCmd; import org.apache.cloudstack.api.command.user.network.UpdateNetworkACLListCmd; @@ -525,6 +531,7 @@ import org.apache.cloudstack.api.command.user.volume.RemoveResourceDetailCmd; 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; @@ -2201,7 +2208,13 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (networkMap == null) { return new Pair<>(addrs, 0); } - _accountMgr.checkAccess(caller, null, false, _accountDao.findById(networkMap.getAccountId())); + try { + _accountMgr.checkAccess(caller, null, false, _accountDao.findById(networkMap.getAccountId())); + } catch (PermissionDeniedException ex) { + s_logger.info("Account " + caller + " do not have permission to access account of network " + network); + _accountMgr.checkAccess(caller, SecurityChecker.AccessType.UseEntry, false, network); + isAllocated = Boolean.TRUE; + } } else { // Domain level NetworkDomainVO networkMap = _networkDomainDao.getDomainNetworkMapByNetworkId(network.getId()); if (networkMap == null) { @@ -3296,6 +3309,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(ListNetworksCmd.class); cmdList.add(RestartNetworkCmd.class); cmdList.add(UpdateNetworkCmd.class); + cmdList.add(CreateNetworkPermissionsCmd.class); + cmdList.add(ListNetworkPermissionsCmd.class); + cmdList.add(RemoveNetworkPermissionsCmd.class); + cmdList.add(ResetNetworkPermissionsCmd.class); cmdList.add(ListDiskOfferingsCmd.class); cmdList.add(ListServiceOfferingsCmd.class); cmdList.add(ActivateProjectCmd.class); @@ -3537,6 +3554,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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); @@ -3559,6 +3577,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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 5b241cb807f..664bb1a4992 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.net.URLEncoder; 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.LinkedHashSet; @@ -106,6 +107,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.IpAddress; import com.cloud.network.IpAddressManager; import com.cloud.network.Network; +import com.cloud.network.NetworkModel; import com.cloud.network.VpnUserVO; import com.cloud.network.as.AutoScaleManager; import com.cloud.network.dao.AccountGuestVlanMapDao; @@ -117,6 +119,7 @@ import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.RemoteAccessVpnDao; import com.cloud.network.dao.RemoteAccessVpnVO; import com.cloud.network.dao.VpnUserDao; +import com.cloud.network.router.VirtualRouter; import com.cloud.network.security.SecurityGroupManager; import com.cloud.network.security.dao.SecurityGroupDao; import com.cloud.network.vpc.Vpc; @@ -255,6 +258,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Inject private VpcManager _vpcMgr; @Inject + private NetworkModel _networkModel; + @Inject private Site2SiteVpnManager _vpnMgr; @Inject private AutoScaleManager _autoscaleMgr; @@ -602,8 +607,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M 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) - && !(entity instanceof AffinityGroup)) { + if (entity.getAccountId() != -1 && domainId != -1 && !(entity instanceof VirtualMachineTemplate) + && !(entity instanceof Network && accessType != null && (accessType == AccessType.UseEntry || accessType == AccessType.OperateEntry)) + && !(entity instanceof AffinityGroup) && !(entity instanceof VirtualRouter)) { List toBeChecked = domains.get(entity.getDomainId()); // for templates, we don't have to do cross domains check if (toBeChecked == null) { @@ -886,7 +892,19 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M 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) { + if (_networkModel.isPrivateGateway(network.getId())) { + continue; + } ReservationContext context = new ReservationContextImpl(null, null, getActiveUser(callerUserId), caller); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index dca068559ab..8c006a2cf0f 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -1466,9 +1466,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new InvalidParameterValueException(nic + " is not a nic on " + vmInstance); } - // Perform account permission check on network - _accountMgr.checkAccess(caller, AccessType.UseEntry, false, network); - // don't delete default NIC on a user VM if (nic.isDefaultNic() && vmInstance.getType() == VirtualMachine.Type.User) { throw new InvalidParameterValueException("Unable to remove nic from " + vmInstance + " in " + network + ", nic is default."); @@ -4000,13 +3997,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } - //relax the check if the caller is admin account - if (caller.getType() != Account.Type.ADMIN) { - if (!(network.getGuestType() == Network.GuestType.Shared && network.getAclType() == ACLType.Domain) - && !(network.getAclType() == ACLType.Account && network.getAccountId() == accountId)) { - throw new InvalidParameterValueException("only shared network or isolated network with the same account_id can be added to vm"); - } - } + _accountMgr.checkAccess(owner, AccessType.UseEntry, false, network); IpAddresses requestedIpPair = null; if (requestedIps != null && !requestedIps.isEmpty()) { diff --git a/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java b/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java index cfc0e0615fe..7a0ea2e9721 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.List; 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.offerings.dao.NetworkOfferingDao; 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 class CreatePrivateNetworkTest { Account account = new AccountVO("testaccount", 1, "networkdomain", Account.Type.NORMAL, 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 class CreatePrivateNetworkTest { /* 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 f2688e7ac1e..8687465f3c6 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 class DedicateGuestVlanRangesTest { 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", Account.Type.NORMAL, UUID.randomUUID().toString()); @@ -195,21 +195,21 @@ public class DedicateGuestVlanRangesTest { 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 @@ public class DedicateGuestVlanRangesTest { 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 @@ public class DedicateGuestVlanRangesTest { 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 @@ public class DedicateGuestVlanRangesTest { 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 @@ public class DedicateGuestVlanRangesTest { 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 6fd57fc04d8..e9c9db3cba2 100644 --- a/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java +++ b/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java @@ -39,6 +39,7 @@ import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.NetworkVO; import com.cloud.network.element.NetworkElement; import com.cloud.network.element.UserDataServiceProvider; +import com.cloud.network.router.VirtualRouter; import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Detail; import com.cloud.offerings.NetworkOfferingVO; @@ -524,6 +525,16 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { } + @Override + public void checkNetworkOperatePermissions(Account owner, Network network) { + + } + + @Override + public void checkRouterPermissions(Account owner, VirtualRouter router) { + + } + /* (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 022181e2217..4a7c2379a98 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 class UpdatePhysicalNetworkTest { 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/network/lb/AssignLoadBalancerTest.java b/server/src/test/java/com/cloud/network/lb/AssignLoadBalancerTest.java index c9323b164d6..231116d67c8 100644 --- a/server/src/test/java/com/cloud/network/lb/AssignLoadBalancerTest.java +++ b/server/src/test/java/com/cloud/network/lb/AssignLoadBalancerTest.java @@ -17,16 +17,18 @@ package com.cloud.network.lb; import com.cloud.domain.DomainVO; -import com.cloud.domain.dao.DomainDao; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.Network; import com.cloud.network.NetworkModelImpl; import com.cloud.network.dao.LoadBalancerDao; import com.cloud.network.dao.LoadBalancerVMMapDao; import com.cloud.network.dao.LoadBalancerVMMapVO; import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.network.dao.NetworkDao; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.RulesManagerImpl; import com.cloud.user.Account; @@ -41,6 +43,7 @@ import com.cloud.vm.NicVO; import com.cloud.vm.UserVmVO; import com.cloud.vm.dao.NicSecondaryIpDao; import com.cloud.vm.dao.UserVmDao; +import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.command.user.loadbalancer.AssignToLoadBalancerRuleCmd; import org.apache.cloudstack.api.response.SuccessResponse; @@ -54,8 +57,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; -import javax.inject.Inject; - import java.util.UUID; import java.util.HashMap; import java.util.Map; @@ -65,31 +66,17 @@ import java.util.ArrayList; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.when; public class AssignLoadBalancerTest { - @Inject - AccountManager _accountMgr; - - @Inject - AccountManager _acctMgr; - - @Inject - AccountDao _accountDao; - - @Inject - DomainDao _domainDao; - @Mock List _lbvmMapList; @Mock List nic; - @Mock - UserVmDao userDao; - @Spy RulesManagerImpl _rulesMgr = new RulesManagerImpl() { @Override @@ -169,20 +156,31 @@ public class AssignLoadBalancerTest { List vmIds = new ArrayList(); vmIds.add(2L); + LoadBalancerVO lbVO = new LoadBalancerVO("1", "L1", "Lbrule", 1, 22, 22, "rb", 204, 0, 0, "tcp"); + UserVmVO vm = new UserVmVO(2L, "test", "test", 101L, Hypervisor.HypervisorType.Any, 21L, false, false, domainId, 200L, 1, 5L, "", "test"); + LoadBalancerDao lbDao = Mockito.mock(LoadBalancerDao.class); LoadBalancerVMMapDao lb2VmMapDao = Mockito.mock(LoadBalancerVMMapDao.class); UserVmDao userVmDao = Mockito.mock(UserVmDao.class); + AccountDao accountDao = Mockito.mock(AccountDao.class); + NetworkDao networkDao = Mockito.mock(NetworkDao.class); + AccountManager accountMgr = Mockito.mock(AccountManager.class); _lbMgr._lbDao = lbDao; _lbMgr._lb2VmMapDao = lb2VmMapDao; _lbMgr._vmDao = userVmDao; + _lbMgr._accountDao = accountDao; + _lbMgr._accountMgr = accountMgr; + _lbMgr._networkDao = networkDao; _lbvmMapList = new ArrayList<>(); _lbMgr._rulesMgr = _rulesMgr; _lbMgr._networkModel = _networkModel; when(lbDao.findById(anyLong())).thenReturn(Mockito.mock(LoadBalancerVO.class)); - when(userVmDao.findById(anyLong())).thenReturn(Mockito.mock(UserVmVO.class)); + when(userVmDao.findById(anyLong())).thenReturn(vm); when(lb2VmMapDao.listByLoadBalancerId(anyLong(), anyBoolean())).thenReturn(_lbvmMapList); + when(accountDao.findById(anyLong())).thenReturn(Mockito.mock(AccountVO.class)); + Mockito.doNothing().when(accountMgr).checkAccess(any(Account.class), any(SecurityChecker.AccessType.class), any(Boolean.class), any(Network.class)); _lbMgr.assignToLoadBalancer(1L, null, vmIdIpMap); } @@ -202,22 +200,29 @@ public class AssignLoadBalancerTest { vmIds.add(2L); LoadBalancerVO lbVO = new LoadBalancerVO("1", "L1", "Lbrule", 1, 22, 22, "rb", 204, 0, 0, "tcp"); + UserVmVO vm = new UserVmVO(2L, "test", "test", 101L, Hypervisor.HypervisorType.Any, 21L, false, false, domainId, 200L, 1, 5L, "", "test"); LoadBalancerDao lbDao = Mockito.mock(LoadBalancerDao.class); LoadBalancerVMMapDao lb2VmMapDao = Mockito.mock(LoadBalancerVMMapDao.class); UserVmDao userVmDao = Mockito.mock(UserVmDao.class); + AccountDao accountDao = Mockito.mock(AccountDao.class); + NetworkDao networkDao = Mockito.mock(NetworkDao.class); + AccountManager accountMgr = Mockito.mock(AccountManager.class); NicSecondaryIpDao nicSecIpDao = Mockito.mock(NicSecondaryIpDao.class); _lbMgr._lbDao = lbDao; _lbMgr._lb2VmMapDao = lb2VmMapDao; _lbMgr._vmDao = userVmDao; + _lbMgr._accountDao = accountDao; + _lbMgr._accountMgr = accountMgr; + _lbMgr._networkDao = networkDao; _lbMgr._nicSecondaryIpDao = nicSecIpDao; _lbvmMapList = new ArrayList<>(); _lbMgr._rulesMgr = _rulesMgr; _lbMgr._networkModel = _networkModel; when(lbDao.findById(anyLong())).thenReturn(lbVO); - when(userVmDao.findById(anyLong())).thenReturn(Mockito.mock(UserVmVO.class)); + when(userVmDao.findById(anyLong())).thenReturn(vm); when(lb2VmMapDao.listByLoadBalancerId(anyLong(), anyBoolean())).thenReturn(_lbvmMapList); when (nicSecIpDao.findByIp4AddressAndNicId(anyString(), anyLong())).thenReturn(null); @@ -240,16 +245,23 @@ public class AssignLoadBalancerTest { vmIds.add(2L); LoadBalancerVO lbVO = new LoadBalancerVO("1", "L1", "Lbrule", 1, 22, 22, "rb", 204, 0, 0, "tcp"); + UserVmVO vm = new UserVmVO(2L, "test", "test", 101L, Hypervisor.HypervisorType.Any, 21L, false, false, domainId, 200L, 1, 5L, "", "test"); LoadBalancerDao lbDao = Mockito.mock(LoadBalancerDao.class); LoadBalancerVMMapDao lb2VmMapDao = Mockito.mock(LoadBalancerVMMapDao.class); UserVmDao userVmDao = Mockito.mock(UserVmDao.class); + AccountDao accountDao = Mockito.mock(AccountDao.class); + AccountManager accountMgr = Mockito.mock(AccountManager.class); + NetworkDao networkDao = Mockito.mock(NetworkDao.class); NicSecondaryIpDao nicSecIpDao = Mockito.mock(NicSecondaryIpDao.class); LoadBalancerVMMapVO lbVmMapVO = new LoadBalancerVMMapVO(1L, 1L, "10.1.1.175", false); _lbMgr._lbDao = lbDao; _lbMgr._lb2VmMapDao = lb2VmMapDao; _lbMgr._vmDao = userVmDao; + _lbMgr._accountDao = accountDao; + _lbMgr._accountMgr = accountMgr; + _lbMgr._networkDao = networkDao; _lbMgr._nicSecondaryIpDao = nicSecIpDao; _lbvmMapList = new ArrayList<>(); _lbvmMapList.add(lbVmMapVO); @@ -257,7 +269,7 @@ public class AssignLoadBalancerTest { _lbMgr._networkModel = _networkModel; when(lbDao.findById(anyLong())).thenReturn(lbVO); - when(userVmDao.findById(anyLong())).thenReturn(Mockito.mock(UserVmVO.class)); + when(userVmDao.findById(anyLong())).thenReturn(vm); when(lb2VmMapDao.listByLoadBalancerId(anyLong(), anyBoolean())).thenReturn(_lbvmMapList); when (nicSecIpDao.findByIp4AddressAndNicId(anyString(), anyLong())).thenReturn(null); diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java index 7301fcf2a46..4941561a929 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java @@ -28,9 +28,14 @@ import org.apache.cloudstack.acl.ControlledEntity.ACLType; 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.CreateNetworkPermissionsCmd; +import org.apache.cloudstack.api.command.user.network.ListNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; +import org.apache.cloudstack.api.command.user.network.RemoveNetworkPermissionsCmd; +import org.apache.cloudstack.api.command.user.network.ResetNetworkPermissionsCmd; import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd; import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd; import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; @@ -49,11 +54,13 @@ import com.cloud.exception.InsufficientVirtualNetworkCapacityException; 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; import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; +import com.cloud.network.NetworkPermission; import com.cloud.network.NetworkProfile; import com.cloud.network.NetworkService; import com.cloud.network.Networks.TrafficType; @@ -318,13 +325,13 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches } @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; } @@ -516,7 +523,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches */ @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; @@ -973,6 +980,11 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches } + @Override + public boolean isSharedNetworkWithoutSpecifyVlan(NetworkOffering offering) { + return false; + } + @Override public IpAddress updateIP(Long id, String customId, Boolean displayIp) { // TODO Auto-generated method stub @@ -1007,4 +1019,29 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches @Override public void unmanageNics(VirtualMachineProfile vm) { } + + @Override + public Pair, Integer> listGuestVlans(ListGuestVlansCmd cmd) { + return null; + } + + @Override + public List listNetworkPermissions(ListNetworkPermissionsCmd listNetworkPermissionsCmd) { + return null; + } + + @Override + public boolean createNetworkPermissions(CreateNetworkPermissionsCmd createNetworkPermissionsCmd) { + return false; + } + + @Override + public boolean removeNetworkPermissions(RemoveNetworkPermissionsCmd removeNetworkPermissionsCmd) { + return false; + } + + @Override + public boolean resetNetworkPermissions(ResetNetworkPermissionsCmd resetNetworkPermissionsCmd) { + return false; + } } diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java index a731f7c7c10..83ffa034761 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java @@ -46,6 +46,7 @@ import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.NetworkVO; import com.cloud.network.element.NetworkElement; import com.cloud.network.element.UserDataServiceProvider; +import com.cloud.network.router.VirtualRouter; import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Detail; import com.cloud.offerings.NetworkOfferingVO; @@ -541,6 +542,16 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { } + @Override + public void checkNetworkOperatePermissions(Account owner, Network network) { + + } + + @Override + public void checkRouterPermissions(Account owner, VirtualRouter router) { + + } + /* (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 c05b732042e..caf2b910a25 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 class CreateNetworkOfferingTest extends TestCase { 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 6a7dbd231c7..6f4ee69f4dd 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.junit4.SpringJUnit4ClassRunner; 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_network_permissions.py b/test/integration/smoke/test_network_permissions.py new file mode 100644 index 00000000000..b6c545f1027 --- /dev/null +++ b/test/integration/smoke/test_network_permissions.py @@ -0,0 +1,760 @@ +# 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 network permissions +""" + +import logging + +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase + +from marvin.lib.base import (Account, + Configurations, + Domain, + Project, + ServiceOffering, + VirtualMachine, + Zone, + Network, + NetworkOffering, + NetworkPermission, + NIC, + PublicIPAddress, + LoadBalancerRule, + NATRule, + StaticNATRule, + + SSHKeyPair) + +from marvin.lib.common import (get_domain, + get_zone, + get_template) + +NETWORK_FILTER_ACCOUNT = 'account' +NETWORK_FILTER_DOMAIN = 'domain' +NETWORK_FILTER_ACCOUNT_DOMAIN = 'accountdomain' +NETWORK_FILTER_SHARED = 'shared' +NETWORK_FILTER_ALL = 'all' + +class TestNetworkPermissions(cloudstackTestCase): + """ + Test user-shared networks + """ + @classmethod + def setUpClass(cls): + cls.testClient = super( + TestNetworkPermissions, + 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("TestNetworkPermissions") + cls.stream_handler = logging.StreamHandler() + cls.logger.setLevel(logging.DEBUG) + cls.logger.addHandler(cls.stream_handler) + + cls.domain = get_domain(cls.apiclient) + + # Create small service offering + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["small"] + ) + cls._cleanup.append(cls.service_offering) + + # Create network offering for isolated networks + cls.network_offering_isolated = NetworkOffering.create( + cls.apiclient, + cls.services["isolated_network_offering"] + ) + cls.network_offering_isolated.update(cls.apiclient, state='Enabled') + cls._cleanup.append(cls.network_offering_isolated) + + # Create sub-domain + cls.sub_domain = Domain.create( + cls.apiclient, + cls.services["acl"]["domain1"] + ) + cls._cleanup.append(cls.sub_domain) + + # 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._cleanup.append(cls.domain_admin) + + cls.network_owner = Account.create( + cls.apiclient, + cls.services["acl"]["accountD11A"], + domainid=cls.sub_domain.id + ) + cls._cleanup.append(cls.network_owner) + + cls.other_user = Account.create( + cls.apiclient, + cls.services["acl"]["accountD11B"], + domainid=cls.sub_domain.id + ) + cls._cleanup.append(cls.other_user) + + # Create project + cls.project = Project.create( + cls.apiclient, + cls.services["project"], + account=cls.domain_admin.name, + domainid=cls.domain_admin.domainid + ) + cls._cleanup.append(cls.project) + + # Create api clients for domain admin and normal user + cls.domainadmin_user = cls.domain_admin.user[0] + cls.domainadmin_apiclient = cls.testClient.getUserApiClient( + cls.domainadmin_user.username, cls.sub_domain.name + ) + cls.networkowner_user = cls.network_owner.user[0] + cls.user_apiclient = cls.testClient.getUserApiClient( + cls.networkowner_user.username, cls.sub_domain.name + ) + + cls.otheruser_user = cls.other_user.user[0] + cls.otheruser_apiclient = cls.testClient.getUserApiClient( + cls.otheruser_user.username, cls.sub_domain.name + ) + + # Create networks for domain admin, normal user and project + cls.services["network"]["name"] = "Test Network Isolated - Project" + cls.project_network = Network.create( + cls.apiclient, + cls.services["network"], + networkofferingid=cls.network_offering_isolated.id, + domainid=cls.sub_domain.id, + projectid=cls.project.id, + zoneid=cls.zone.id + ) + cls._cleanup.append(cls.project_network) + + cls.services["network"]["name"] = "Test Network Isolated - Domain admin" + cls.domainadmin_network = Network.create( + cls.apiclient, + cls.services["network"], + networkofferingid=cls.network_offering_isolated.id, + domainid=cls.sub_domain.id, + accountid=cls.domain_admin.name, + zoneid=cls.zone.id + ) + cls._cleanup.append(cls.domainadmin_network) + + cls.services["network"]["name"] = "Test Network Isolated - Normal user" + cls.user_network = Network.create( + cls.apiclient, + cls.services["network"], + networkofferingid=cls.network_offering_isolated.id, + domainid=cls.sub_domain.id, + accountid=cls.network_owner.name, + zoneid=cls.zone.id + ) + cls._cleanup.append(cls.user_network) + + @classmethod + def tearDownClass(cls): + super(TestNetworkPermissions, cls).tearDownClass() + + def setUp(self): + self.cleanup = [] + self.virtual_machine = None + + def tearDown(self): + super(TestNetworkPermissions, self).tearDown() + + def list_network(self, apiclient, account, network, project, network_filter=None, expected=True): + # List networks by apiclient, account, network, project and network network_filter + # If account is specified, list the networks which can be used by the domain (canusefordeploy=true,listall=false) + # otherwise canusefordeploy is None and listall is True. + domain_id = None + account_name = None + project_id = None + canusefordeploy = None + list_all = True + if account: + domain_id = account.domainid + account_name = account.name + canusefordeploy = True + list_all = False + if project: + project_id = project.id + networks = None + try: + networks = Network.list( + apiclient, + canusefordeploy=canusefordeploy, + listall=list_all, + networkfilter= network_filter, + domainid=domain_id, + account=account_name, + projectid=project_id, + id=network.id + ) + if isinstance(networks, list) and len(networks) > 0: + if not expected: + self.fail("Found the network, but expected to fail") + elif expected: + self.fail("Failed to find the network, but expected to succeed") + except Exception as ex: + networks = None + if expected: + self.fail(f"Failed to list network, but expected to succeed : {ex}") + if networks and not expected: + self.fail("network is listed successfully, but expected to fail") + + def list_network_by_filters(self, apiclient, account, network, project, expected_results=None): + # expected results in order: account/domain/accountdomain/shared/all + self.list_network(apiclient, account, network, project, NETWORK_FILTER_ACCOUNT, expected_results[0]) + self.list_network(apiclient, account, network, project, NETWORK_FILTER_DOMAIN, expected_results[1]) + self.list_network(apiclient, account, network, project, NETWORK_FILTER_ACCOUNT_DOMAIN, expected_results[2]) + self.list_network(apiclient, account, network, project, NETWORK_FILTER_SHARED, expected_results[3]) + self.list_network(apiclient, account, network, project, NETWORK_FILTER_ALL, expected_results[4]) + + def create_network_permission(self, apiclient, network, account, project, expected=True): + account_id = None + project_id = None + if account: + account_id = account.id + if project: + project_id = project.id + result = True + try: + NetworkPermission.create( + apiclient, + networkid=network.id, + accountids=account_id, + projectids=project_id + ) + except Exception as ex: + result = False + if expected: + self.fail(f"Failed to create network permissions, but expected to succeed : {ex}") + if result and not expected: + self.fail("network permission is created successfully, but expected to fail") + + def remove_network_permission(self, apiclient, network, account, project, expected=True): + account_id = None + project_id = None + if account: + account_id = account.id + if project: + project_id = project.id + result = True + try: + NetworkPermission.remove( + apiclient, + networkid=network.id, + accountids=account_id, + projectids=project_id + ) + except Exception as ex: + result = False + if expected: + self.fail(f"Failed to remove network permissions, but expected to succeed : {ex}") + if result and not expected: + self.fail("network permission is removed successfully, but expected to fail") + + def reset_network_permission(self, apiclient, network, expected=True): + result = True + try: + NetworkPermission.reset( + apiclient, + networkid=network.id + ) + except Exception as ex: + result = False + if expected: + self.fail(f"Failed to reset network permissions, but expected to succeed : {ex}") + if result and not expected: + self.fail("network permission is reset successfully, but expected to fail") + + def exec_command(self, apiclient_str, command, expected=None): + result = True + try: + command = command.format(apiclient = apiclient_str) + exec(command) + except Exception as ex: + result = False + if expected: + self.fail(f"Failed to execute command '{command}' with exception : {ex}") + if result and expected is False: + self.fail(f"command {command} is executed successfully, but expected to fail") + if expected is None: + # if expected is None, display the command and result + self.logger.info(f"Result of command '{command}' : {result}") + return result + + @attr(tags=["advanced"], required_hardware="false") + def test_01_network_permission_on_project_network(self): + """ Testing network permissions on project network """ + + self.create_network_permission(self.apiclient, self.project_network, self.domain_admin, None, expected=False) + self.create_network_permission(self.domainadmin_apiclient, self.project_network, self.domain_admin, None, expected=False) + self.create_network_permission(self.user_apiclient, self.project_network, self.network_owner, None, expected=False) + + @attr(tags=["advanced"], required_hardware="false") + def test_02_network_permission_on_user_network(self): + """ Testing network permissions on user network """ + + # List user network by domain admin + self.list_network_by_filters(self.domainadmin_apiclient, None, self.user_network, None, [True, False, True, False, True]) + self.list_network_by_filters(self.domainadmin_apiclient, self.domain_admin, self.user_network, None, [False, False, False, False, False]) + + # Create network permissions + self.create_network_permission(self.apiclient, self.user_network, self.domain_admin, None, expected=True) + self.create_network_permission(self.domainadmin_apiclient, self.user_network, self.domain_admin, None, expected=True) + self.create_network_permission(self.user_apiclient, self.user_network, self.network_owner, None, expected=True) + self.create_network_permission(self.user_apiclient, self.user_network, self.other_user, None, expected=True) + self.create_network_permission(self.user_apiclient, self.user_network, None, self.project, expected=False) + self.create_network_permission(self.domainadmin_apiclient, self.user_network, None, self.project, expected=True) + self.create_network_permission(self.otheruser_apiclient, self.user_network, self.network_owner, None, expected=False) + + # List domain admin network by domain admin + self.list_network_by_filters(self.domainadmin_apiclient, None, self.domainadmin_network, None, [True, False, True, False, True]) + self.list_network_by_filters(self.domainadmin_apiclient, self.domain_admin, self.domainadmin_network, None, [True, False, True, False, True]) + # List user network by domain admin + self.list_network_by_filters(self.domainadmin_apiclient, None, self.user_network, None, [True, False, True, True, True]) + self.list_network_by_filters(self.domainadmin_apiclient, self.domain_admin, self.user_network, None, [False, False, False, True, True]) + # List user network by user + self.list_network_by_filters(self.user_apiclient, None, self.user_network, None, [True, False, True, False, True]) + self.list_network_by_filters(self.user_apiclient, self.network_owner, self.user_network, None, [True, False, True, False, True]) + # List user network by other user + self.list_network_by_filters(self.otheruser_apiclient, None, self.user_network, None, [False, False, False, True, True]) + self.list_network_by_filters(self.otheruser_apiclient, self.network_owner, self.user_network, None, [False, False, False, False, False]) + + # Remove network permissions + self.remove_network_permission(self.domainadmin_apiclient, self.user_network, self.domain_admin, None, expected=True) + # List user network by domain admin + self.list_network_by_filters(self.domainadmin_apiclient, None, self.user_network, None, [True, False, True, True, True]) + self.list_network_by_filters(self.domainadmin_apiclient, self.domain_admin, self.user_network, None, [False, False, False, False, False]) + + # Reset network permissions + self.reset_network_permission(self.domainadmin_apiclient, self.user_network, expected=True) + # List user network by domain admin + self.list_network_by_filters(self.domainadmin_apiclient, None, self.user_network, None, [True, False, True, False, True]) + self.list_network_by_filters(self.domainadmin_apiclient, self.domain_admin, self.user_network, None, [False, False, False, False, False]) + + @attr(tags=["advanced"], required_hardware="false") + def test_03_network_operations_on_created_vm_of_otheruser(self): + """ Testing network operations on a create vm owned by other user""" + + # 1. Create an Isolated network by other user + self.services["network"]["name"] = "Test Network Isolated - Other user" + otheruser_network = Network.create( + self.otheruser_apiclient, + self.services["network"], + networkofferingid=self.network_offering_isolated.id, + zoneid=self.zone.id + ) + self.cleanup.append(otheruser_network) + + # 2. Deploy vm1 on other user's network + self.virtual_machine = VirtualMachine.create( + self.otheruser_apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + networkids=otheruser_network.id, + zoneid=self.zone.id + ) + + # 3. Add user network to vm1, should fail by vm owner and network owner + command = """self.virtual_machine.add_nic({apiclient}, self.user_network.id)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=False) + + # 4. Create network permission for other user, should succeed by network owner + command = """self.create_network_permission({apiclient}, self.user_network, self.other_user, None, expected=True)""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=True) + + # 5. Add user network to vm1, should succeed by vm owner + command = """self.virtual_machine.add_nic({apiclient}, self.user_network.id)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 6. Stop vm1 with forced=true, should succeed by vm owner + command = """self.virtual_machine.stop({apiclient}, forced=True)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # Get id of the additional nic + list_vms = VirtualMachine.list( + self.otheruser_apiclient, + id = self.virtual_machine.id + ) + self.assertEqual( + isinstance(list_vms, list), + True, + "Check if virtual machine is present" + ) + self.assertEqual( + len(list_vms) > 0, + True, + "Check if virtual machine list is empty" + ) + self.vm_default_nic_id = None + self.vm_new_nic_id = None + for vm_nic in list_vms[0].nic: + if vm_nic.networkid == self.user_network.id: + self.vm_new_nic_id = vm_nic.id + else: + self.vm_default_nic_id = vm_nic.id + + # 6. Update vm1 nic IP, should succeed by vm owner + command = """NIC.updateIp({apiclient}, self.vm_new_nic_id)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 7. Start vm1, should succeed by vm owner + command = """self.virtual_machine.start({apiclient})""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 8. Add secondary IP to nic, should succeed by vm owner + command = """self.secondaryip = NIC.addIp({apiclient}, self.vm_new_nic_id)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 9 Remove secondary IP from nic, should succeed by vm owner + command = """NIC.removeIp({apiclient}, self.secondaryip.id)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 10. Update default NIC, should succeed by vm owner + command = """self.virtual_machine.update_default_nic({apiclient}, self.vm_new_nic_id)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + command = """self.virtual_machine.update_default_nic({apiclient}, self.vm_default_nic_id)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 11. Stop vm1 with forced=true + command = """self.virtual_machine.stop({apiclient}, forced=True)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 12. Remove nic from vm1, should succeed by vm owner + command = """self.virtual_machine.remove_nic({apiclient}, self.vm_new_nic_id)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 13. Test operations by domain admin + command = """self.virtual_machine.add_nic({apiclient}, self.user_network.id)""" + self.exec_command("self.domainadmin_apiclient", command, expected=True) + + list_vms = VirtualMachine.list( + self.otheruser_apiclient, + id = self.virtual_machine.id + ) + + self.vm_default_nic_id = None + self.vm_new_nic_id = None + for vm_nic in list_vms[0].nic: + if vm_nic.networkid == self.user_network.id: + self.vm_new_nic_id = vm_nic.id + else: + self.vm_default_nic_id = vm_nic.id + + command = """NIC.updateIp({apiclient}, self.vm_new_nic_id)""" + self.exec_command("self.domainadmin_apiclient", command, expected=True) + + command = """self.secondaryip = NIC.addIp({apiclient}, self.vm_new_nic_id)""" + self.exec_command("self.domainadmin_apiclient", command, expected=True) + + command = """NIC.removeIp({apiclient}, self.secondaryip.id)""" + self.exec_command("self.domainadmin_apiclient", command, expected=True) + + command = """self.virtual_machine.update_default_nic({apiclient}, self.vm_new_nic_id)""" + self.exec_command("self.domainadmin_apiclient", command, expected=True) + + command = """self.virtual_machine.update_default_nic({apiclient}, self.vm_default_nic_id)""" + self.exec_command("self.domainadmin_apiclient", command, expected=True) + + command = """self.virtual_machine.remove_nic({apiclient}, self.vm_new_nic_id)""" + self.exec_command("self.domainadmin_apiclient", command, expected=True) + + # 14. Test operations by vm owner, when network permission is removed + command = """self.virtual_machine.add_nic({apiclient}, self.user_network.id)""" + self.exec_command("self.domainadmin_apiclient", command, expected=True) + + # 15. Reset network permissions, should succeed by network owner + command = """self.reset_network_permission({apiclient}, self.user_network, expected=True)""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=True) + + list_vms = VirtualMachine.list( + self.otheruser_apiclient, + id = self.virtual_machine.id + ) + + self.vm_default_nic_id = None + self.vm_new_nic_id = None + for vm_nic in list_vms[0].nic: + if vm_nic.networkid == self.user_network.id: + self.vm_new_nic_id = vm_nic.id + else: + self.vm_default_nic_id = vm_nic.id + + command = """NIC.updateIp({apiclient}, self.vm_new_nic_id)""" + self.exec_command("self.otheruser_apiclient", command, expected=True) + + command = """self.secondaryip = NIC.addIp({apiclient}, self.vm_new_nic_id)""" + if self.exec_command("self.otheruser_apiclient", command, expected=True): + command = """NIC.removeIp({apiclient}, self.secondaryip.id)""" + self.exec_command("self.otheruser_apiclient", command, expected=True) + + command = """self.virtual_machine.update_default_nic({apiclient}, self.vm_new_nic_id)""" + if self.exec_command("self.otheruser_apiclient", command, expected=True): + command = """self.virtual_machine.update_default_nic({apiclient}, self.vm_default_nic_id)""" + self.exec_command("self.otheruser_apiclient", command, expected=True) + + command = """self.virtual_machine.start({apiclient})""" + if self.exec_command("self.otheruser_apiclient", command, expected=True): + command = """self.virtual_machine.stop({apiclient}, forced=True)""" + self.exec_command("self.otheruser_apiclient", command, expected=True) + + command = """self.virtual_machine.remove_nic({apiclient}, self.vm_new_nic_id)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + #self.exec_command("self.domainadmin_apiclient", command, expected=True) + + # 16. Destroy vm1, should succeed by root admin + self.virtual_machine.delete(self.apiclient, expunge=True) + + @attr(tags=["advanced"], required_hardware="false") + def test_04_deploy_vm_for_other_user_and_test_vm_operations(self): + """ Deploy VM for other user and test VM operations by vm owner, network owner and domain admin""" + + # 1. Create network permission for other user, by user + command = """self.create_network_permission({apiclient}, self.user_network, self.other_user, None, expected=True)""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=True) + + # 2. Deploy vm2 on user network + command = """self.virtual_machine = VirtualMachine.create( + {apiclient}, + self.services["virtual_machine"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + networkids=self.user_network.id, + accountid=self.other_user.name, + domainid=self.other_user.domainid, + zoneid=self.zone.id + )""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + if not self.virtual_machine: + self.fail("Failed to find self.virtual_machine") + + # 3. List vm2 + list_vms = VirtualMachine.list( + self.user_apiclient, + id = self.virtual_machine.id + ) + self.assertEqual( + isinstance(list_vms, list) and len(list_vms) > 0, + False, + "Check if virtual machine is not present" + ) + list_vms = VirtualMachine.list( + self.otheruser_apiclient, + id = self.virtual_machine.id + ) + self.assertEqual( + isinstance(list_vms, list) and len(list_vms) > 0, + True, + "Check if virtual machine is present" + ) + + # 4. Stop vm2 with forced=true + command = """self.virtual_machine.stop({apiclient}, forced=True)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 5. Reset vm password + if self.template.passwordenabled: + command = """self.virtual_machine.resetPassword({apiclient})""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 6. Reset vm SSH key + self.keypair = SSHKeyPair.create( + self.otheruser_apiclient, + name=self.other_user.name + ".pem" + ) + command = """self.virtual_machine.resetSshKey({apiclient}, keypair=self.keypair.name)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 7. Start vm2 + command = """self.virtual_machine.start({apiclient})""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 8. Acquire public IP, should succeed by domain admin and network owner + command = """self.public_ip = PublicIPAddress.create( + {apiclient}, + zoneid=self.zone.id, + networkid=self.user_network.id + )""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=True) + #self.exec_command("self.domainadmin_apiclient", command, expected=True) + + # 9. Enable static nat, should succeed by domain admin + command = """StaticNATRule.enable( + {apiclient}, + ipaddressid=self.public_ip.ipaddress.id, + virtualmachineid=self.virtual_machine.id + )""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.domainadmin_apiclient", command, expected=True) + + # 10. Disable static nat, should succeed by domain admin and network owner + command = """StaticNATRule.disable( + {apiclient}, + ipaddressid=self.public_ip.ipaddress.id + )""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=True) + #self.exec_command("self.domainadmin_apiclient", command, expected=True) + + # 11. Create port forwarding rule, should succeed by domain admin + command = """self.port_forwarding_rule = NATRule.create( + {apiclient}, + virtual_machine=self.virtual_machine, + services=self.services["natrule"], + ipaddressid=self.public_ip.ipaddress.id, + )""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.domainadmin_apiclient", command, expected=True) + + # 12. Delete port forwarding rule, should succeed by domain admin and network owner + command = """self.port_forwarding_rule.delete({apiclient})""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=True) + #self.exec_command("self.domainadmin_apiclient", command, expected=True) + + # 13. Create load balancer rule, should succeed by domain admin and network owner + command = """self.load_balancer_rule = LoadBalancerRule.create( + {apiclient}, + self.services["lbrule"], + ipaddressid=self.public_ip.ipaddress.id, + networkid=self.user_network.id, + )""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=True) + #self.exec_command("self.domainadmin_apiclient", command, expected=True) + + # 14. Assign virtual machine to load balancing rule, should succeed by domain admin + command = """self.load_balancer_rule.assign({apiclient}, vms=[self.virtual_machine])""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.domainadmin_apiclient", command, expected=True) + + # 15. Remove virtual machine from load balancing rule, should succeed by domain admin and network owner + command = """self.load_balancer_rule.remove({apiclient}, vms=[self.virtual_machine])""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=True) + #self.exec_command("self.domainadmin_apiclient", command, expected=True) + + # 16. Delete load balancing rule, should succeed by domain admin and network owner + command = """self.load_balancer_rule.delete({apiclient})""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=True) + #self.exec_command("self.domainadmin_apiclient", command, expected=True) + + # 17. Release public IP, should succeed by domain admin and network owner + command = """self.public_ip.delete({apiclient})""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=True) + #self.exec_command("self.domainadmin_apiclient", command, expected=True) + + # 18. Stop vm2 with forced=true, should succeed by vm owner + command = """self.virtual_machine.stop({apiclient}, forced=True)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 19. Update vm2, should succeed by vm owner + command = """self.virtual_machine.update({apiclient}, displayname = self.virtual_machine.displayname + ".new")""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 20. Restore vm2, should succeed by vm owner + command = """self.virtual_machine.restore({apiclient})""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 21. Scale vm2 to another offering, should succeed by vm owner + self.service_offering_new = ServiceOffering.create( + self.apiclient, + self.services["service_offerings"]["big"] + ) + self.cleanup.append(self.service_offering_new) + command = """self.virtual_machine.scale_virtualmachine({apiclient}, self.service_offering_new.id)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 22. Destroy vm2, should succeed by vm owner + command = """self.virtual_machine.delete({apiclient}, expunge=False)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 23. Recover vm2, should succeed by vm owner + allow_expunge_recover_vm = Configurations.list(self.apiclient, name="allow.user.expunge.recover.vm")[0].value + self.logger.debug("Global configuration allow.user.expunge.recover.vm = %s", allow_expunge_recover_vm) + if allow_expunge_recover_vm == "true": + command = """self.virtual_machine.recover({apiclient})""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 24. Destroy vm2, should succeed by vm owner + command = """self.virtual_machine.delete({apiclient}, expunge=False)""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + + # 25. Expunge vm2, should succeed by vm owner + if allow_expunge_recover_vm == "true": + command = """self.virtual_machine.expunge({apiclient})""" + self.exec_command("self.user_apiclient", command, expected=False) + self.exec_command("self.otheruser_apiclient", command, expected=True) + else: + self.virtual_machine.expunge(self.apiclient) + + # 26. Reset network permissions, should succeed by network owner + command = """self.reset_network_permission({apiclient}, self.user_network, expected=True)""" + self.exec_command("self.otheruser_apiclient", command, expected=False) + self.exec_command("self.user_apiclient", command, expected=True) diff --git a/test/integration/smoke/test_pvlan.py b/test/integration/smoke/test_pvlan.py index d7d5d3efb03..45b001a2718 100644 --- a/test/integration/smoke/test_pvlan.py +++ b/test/integration/smoke/test_pvlan.py @@ -30,12 +30,27 @@ _multiprocess_shared_ = True 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_user_private_gateway.py b/test/integration/smoke/test_user_private_gateway.py new file mode 100644 index 00000000000..be6e89c54bd --- /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 small service offering + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["small"] + ) + cls._cleanup.append(cls.service_offering) + + # 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') + cls._cleanup.append(cls.network_offering_isolated) + + # Create vpc offering + cls.vpc_offering = VpcOffering.create( + cls.apiclient, + cls.services["vpc_offering_multi_lb"]) + cls.vpc_offering.update(cls.apiclient, state='Enabled') + cls._cleanup.append(cls.vpc_offering) + + # 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') + cls._cleanup.append(cls.network_offering_vpc) + + # Create sub-domain + cls.sub_domain = Domain.create( + cls.apiclient, + cls.services["acl"]["domain1"] + ) + cls._cleanup.append(cls.sub_domain) + + # 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._cleanup.append(cls.domain_admin) + + cls.normal_user = Account.create( + cls.apiclient, + cls.services["acl"]["accountD1B"], + domainid=cls.sub_domain.id + ) + cls._cleanup.append(cls.normal_user) + + # Create project + cls.project = Project.create( + cls.apiclient, + cls.services["project"], + account=cls.domain_admin.name, + domainid=cls.domain_admin.domainid + ) + cls._cleanup.append(cls.project) + + # 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 + ) + + @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 00000000000..8205425090b --- /dev/null +++ b/test/integration/smoke/test_user_shared_network.py @@ -0,0 +1,631 @@ +# 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 small service offering + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["small"] + ) + cls._cleanup.append(cls.service_offering) + + # 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') + cls._cleanup.append(cls.network_offering_withvlan) + + # 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') + cls._cleanup.append(cls.network_offering_novlan) + + # 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') + cls._cleanup.append(cls.network_offering_isolated) + + # Create vpc offering + cls.vpc_offering = VpcOffering.create( + cls.apiclient, + cls.services["vpc_offering_multi_lb"]) + cls.vpc_offering.update(cls.apiclient, state='Enabled') + cls._cleanup.append(cls.vpc_offering) + + # 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') + cls._cleanup.append(cls.network_offering_vpc) + + # Create sub-domain + cls.sub_domain = Domain.create( + cls.apiclient, + cls.services["acl"]["domain1"] + ) + cls._cleanup.append(cls.sub_domain) + + # 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._cleanup.append(cls.domain_admin) + + cls.normal_user = Account.create( + cls.apiclient, + cls.services["acl"]["accountD1B"], + domainid=cls.sub_domain.id + ) + cls._cleanup.append(cls.normal_user) + + # Create project + cls.project = Project.create( + cls.apiclient, + cls.services["project"], + account=cls.domain_admin.name, + domainid=cls.domain_admin.domainid + ) + cls._cleanup.append(cls.project) + + # 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 + ) + + @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 115c08b9264..4d18760ae94 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -3143,7 +3143,7 @@ class Network: 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"] @@ -3211,6 +3211,8 @@ class Network: cmd.externalid = externalid if bypassvlanoverlapcheck: cmd.bypassvlanoverlapcheck = bypassvlanoverlapcheck + if associatednetworkid: + cmd.associatednetworkid = associatednetworkid return Network(apiclient.createNetwork(cmd).__dict__) def delete(self, apiclient): @@ -4731,14 +4733,15 @@ class PrivateGateway: @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 @@ -4746,6 +4749,8 @@ class PrivateGateway: cmd.aclid = aclid if bypassvlanoverlapcheck: cmd.bypassvlanoverlapcheck = bypassvlanoverlapcheck + if associatednetworkid: + cmd.associatednetworkid = associatednetworkid return PrivateGateway(apiclient.createPrivateGateway(cmd).__dict__) @@ -5301,7 +5306,7 @@ class NIC: """Remove secondary Ip from NIC""" cmd = removeIpFromNic.removeIpFromNicCmd() cmd.id = ipaddressid - return (apiclient.addIpToNic(cmd)) + return (apiclient.removeIpFromNic(cmd)) @classmethod def list(cls, apiclient, **kwargs): @@ -5313,6 +5318,14 @@ class NIC: cmd.listall = True return (apiclient.listNics(cmd)) + @classmethod + def updateIp(cls, apiclient, id, ipaddress=None): + """Update Ip for NIC""" + cmd = updateVmNicIp.updateVmNicIpCmd() + cmd.nicid = id + if ipaddress: + cmd.ipaddress = ipaddress + return (apiclient.updateVmNicIp(cmd)) class SimulatorMock: """Manage simulator mock lifecycle""" @@ -5656,3 +5669,40 @@ class ProjectRolePermission: cmd.projectid = projectid [setattr(cmd, k, v) for k, v in list(kwargs.items())] return (apiclient.listProjectRolePermissions(cmd)) + +class NetworkPermission: + """Manage Network Permission""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, **kwargs): + """Creates network permissions""" + cmd = createNetworkPermissions.createNetworkPermissionsCmd() + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return (apiclient.createNetworkPermissions(cmd)) + + @classmethod + def remove(cls, apiclient, **kwargs): + """Removes the network permissions""" + + cmd = removeNetworkPermissions.removeNetworkPermissionsCmd() + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return (apiclient.removeNetworkPermissions(cmd)) + + @classmethod + def reset(cls, apiclient, **kwargs): + """Updates the network permissions""" + + cmd = resetNetworkPermissions.resetNetworkPermissionsCmd() + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return (apiclient.resetNetworkPermissions(cmd)) + + @classmethod + def list(cls, apiclient, **kwargs): + """List all role permissions matching criteria""" + + cmd = listNetworkPermissions.listNetworkPermissionsCmd() + [setattr(cmd, k, v) for k, v in list(kwargs.items())] + return (apiclient.listNetworkPermissions(cmd)) diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index a61d3442ebf..eb8cca525d1 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -119,6 +119,7 @@ "label.action.delete.load.balancer": "Delete load balancer rule", "label.action.delete.load.balancer.processing": "Deleting Load Balancer....", "label.action.delete.network": "Delete Network", +"label.action.delete.network.permission": "Delete Network Permission", "label.action.delete.network.processing": "Deleting Network....", "label.action.delete.nexusvswitch": "Delete Nexus 1000v", "label.action.delete.nic": "Remove NIC", @@ -250,6 +251,7 @@ "label.action.remove.host.processing": "Removing Host....", "label.action.remove.vm": "Release VM", "label.action.reserve.ip": "Reserve Public IP", +"label.action.reset.network.permissions": "Reset Network Permissions", "label.action.reset.password": "Reset Password", "label.action.reset.password.processing": "Resetting Password....", "label.action.resize.volume": "Resize Volume", @@ -344,6 +346,7 @@ "label.add.network.acl.list": "Add Network ACL List", "label.add.network.device": "Add Network Device", "label.add.network.offering": "Add Network Offering", +"label.add.network.permission": "Add Network Permission", "label.add.new.f5": "Add new F5", "label.add.new.gateway": "Add new gateway", "label.add.new.iso": "Add new ISO", @@ -439,6 +442,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", @@ -1045,6 +1049,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", @@ -1516,6 +1521,7 @@ "label.network.offering.display.text": "Network Offering Display Text", "label.network.offering.name": "Network Offering Name", "label.network.offerings": "Network Offerings", +"label.network.permissions": "Network Permissions", "label.network.selection": "Network Selection", "label.network.service.providers": "Network Service Providers", "label.networkcidr": "Network CIDR", @@ -1665,6 +1671,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", @@ -2192,6 +2199,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", @@ -2773,11 +2781,13 @@ "message.confirm.remove.ip.range": "Please confirm that you would like to remove this IP range.", "message.confirm.remove.load.balancer": "Please confirm you want to remove VM from load balancer", "message.confirm.remove.network.offering": "Are you sure you want to remove this network offering?", +"message.confirm.remove.network.permission": "Are you sure you want to remove this network permission?", "message.confirm.remove.selected.alerts": "Please confirm you would like to remove the selected alerts", "message.confirm.remove.selected.events": "Please confirm you would like to remove the selected events", "message.confirm.remove.vmware.datacenter": "Please confirm you want to remove VMware datacenter", "message.confirm.remove.vpc.offering": "Are you sure you want to remove this VPC offering?", "message.confirm.replace.acl.new.one": "Do you want to replace the ACL with a new one?", +"message.confirm.reset.network.permissions": "Are you sure you want to reset this network permissions?", "message.confirm.scale.up.router.vm": "Do you really want to scale up the Router VM ?", "message.confirm.scale.up.system.vm": "Do you really want to scale up the system VM ?", "message.confirm.shutdown.provider": "Please confirm that you would like to shutdown this provider", @@ -3288,6 +3298,7 @@ "message.setup.physical.network.during.zone.creation": "When adding a zone, you need to set up one or more physical networks. Each network corresponds to a NIC on the hypervisor. Each physical network can carry one or more types of traffic, with certain restrictions on how they may be combined. Add or remove one or more traffic types onto each physical network.", "message.setup.physical.network.during.zone.creation.basic": "When adding a basic zone, you can set up one physical network, which corresponds to a NIC on the hypervisor. The network carries several types of traffic.

You may also add other traffic types onto the physical network.", "message.setup.successful": "Cloud setup successful!", +"message.shared.network.offering.warning": "Domain admins and regular users can only create Shared networks from network offering with specifyvlan=false. Please contact admin to create a network offering if this list is empty.", "message.specify.tag.key": "Please specify a tag key", "message.specify.tag.key.value": "Please specify a tag key and value", "message.specify.tag.value": "Please specify a tag value", @@ -3307,6 +3318,7 @@ "message.success.add.kuberversion": "Successfully added Kubernetes version", "message.success.add.network": "Successfully added network", "message.success.add.network.acl": "Successfully added Network ACL List", +"message.success.add.network.permissions": "Successfully added Network Permissions", "message.success.add.port.forward": "Successfully added new Port Forwarding rule", "message.success.add.private.gateway": "Successfully added Private Gateway", "message.success.add.rule": "Successfully added new rule", @@ -3362,11 +3374,13 @@ "message.success.remove.instance.rule": "Successfully removed instance from rule", "message.success.remove.ip": "Successfully removed IP", "message.success.remove.iprange": "Successfully removed IP Range", +"message.success.remove.network.permissions": "Successfully removed Network Permissions", "message.success.remove.nic": "Successfully removed", "message.success.remove.port.forward": "Successfully removed Port Forwarding rule", "message.success.remove.rule": "Successfully deleted rule", "message.success.remove.secondary.ipaddress": "Successfully removed secondary IP Address", "message.success.remove.sticky.policy": "Successfully removed sticky policy", +"message.success.reset.network.permissions": "Successfully reset Network Permissions", "message.success.resize.volume": "Successfully resized volume", "message.success.scale.kubernetes": "Successfully scaled Kubernetes cluster", "message.success.unmanage.instance": "Successfully unmanaged instance", diff --git a/ui/src/components/view/InfoCard.vue b/ui/src/components/view/InfoCard.vue index a1e4ef381b8..014b4d5c12a 100644 --- a/ui/src/components/view/InfoCard.vue +++ b/ui/src/components/view/InfoCard.vue @@ -385,7 +385,7 @@
{{ $t('label.associatednetwork') }}
- {{ resource.associatednetworkname || resource.associatednetworkid }} + {{ resource.associatednetworkname || resource.associatednetwork || resource.associatednetworkid }}
diff --git a/ui/src/components/view/ListView.vue b/ui/src/components/view/ListView.vue index e05e5ab1dc8..7899d88fb2a 100644 --- a/ui/src/components/view/ListView.vue +++ b/ui/src/components/view/ListView.vue @@ -195,6 +195,11 @@ + diff --git a/ui/src/components/view/SearchView.vue b/ui/src/components/view/SearchView.vue index 99762a50524..ac1961b841a 100644 --- a/ui/src/components/view/SearchView.vue +++ b/ui/src/components/view/SearchView.vue @@ -250,7 +250,7 @@ export default { if (item === 'clusterid' && !('listClusters' in this.$store.getters.apis)) { return true } - if (['zoneid', 'domainid', 'state', 'level', 'clusterid', 'podid', 'entitytype'].includes(item)) { + if (['zoneid', 'domainid', 'state', 'level', 'clusterid', 'podid', 'entitytype', 'type'].includes(item)) { type = 'list' } else if (item === 'tags') { type = 'tag' @@ -271,6 +271,15 @@ export default { let podIndex = -1 let clusterIndex = -1 + if (arrayField.includes('type')) { + if (this.$route.path === '/guestnetwork' || this.$route.path.includes('/guestnetwork/')) { + const typeIndex = this.fields.findIndex(item => item.name === 'type') + this.fields[typeIndex].loading = true + this.fields[typeIndex].opts = this.fetchGuestNetworkTypes() + this.fields[typeIndex].loading = false + } + } + if (arrayField.includes('state')) { const stateIndex = this.fields.findIndex(item => item.name === 'state') this.fields[stateIndex].loading = true @@ -431,6 +440,24 @@ export default { }) }) }, + fetchGuestNetworkTypes () { + const types = [] + if (this.apiName.indexOf('listNetworks') > -1) { + types.push({ + id: 'Isolated', + name: 'label.isolated' + }) + types.push({ + id: 'Shared', + name: 'label.shared' + }) + types.push({ + id: 'L2', + name: 'label.l2' + }) + } + return types + }, fetchState () { const state = [] if (this.apiName.indexOf('listVolumes') > -1) { diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index 7c90c077446..e0d631f0619 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -39,14 +39,14 @@ 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', 'egressdefaultpolicy', 'zonename', 'account', 'domain'] + var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domain', 'associatednetwork', 'associatednetworkid'] if (!isAdmin()) { fields = fields.filter(function (e) { return e !== 'broadcasturi' }) } return fields }, - filters: ['all', 'isolated', 'shared', 'l2'], - searchFilters: ['keyword', 'zoneid', 'domainid', 'account', 'tags'], + filters: ['all', 'account', 'domain', 'shared'], + searchFilters: ['keyword', 'zoneid', 'domainid', 'account', 'type', 'tags'], related: [{ name: 'vm', title: 'label.instances', @@ -71,6 +71,10 @@ export default { name: 'guest.ip.range', component: shallowRef(defineAsyncComponent(() => import('@/views/network/GuestIpRanges.vue'))), show: (record) => { return 'listVlanIpRanges' in store.getters.apis && (record.type === 'Shared' || (record.service && record.service.filter(x => x.name === 'SourceNat').count === 0)) } + }, { + name: 'network.permissions', + component: shallowRef(defineAsyncComponent(() => import('@/views/network/NetworkPermissions.vue'))), + show: (record, route, user) => { return 'listNetworkPermissions' in store.getters.apis && record.acltype === 'Account' && !('vpcid' in record) && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account) } }, { name: 'comments', @@ -372,7 +376,7 @@ export default { hidden: true, permission: ['listPrivateGateways'], columns: ['ipaddress', 'state', 'gateway', 'netmask', 'account'], - details: ['ipaddress', 'gateway', 'netmask', 'vlan', 'sourcenatsupported', 'aclname', 'account', 'domain', 'zone'], + details: ['ipaddress', 'gateway', 'netmask', 'vlan', 'sourcenatsupported', 'aclname', 'account', 'domain', 'zone', 'associatednetwork', 'associatednetworkid'], tabs: [{ name: 'details', component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) @@ -707,6 +711,31 @@ export default { groupMap: (selection) => { return selection.map(x => { return { id: x } }) } } ] + }, + { + name: 'guestvlans', + title: 'label.guest.vlan', + icon: 'folder-outlined', + 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: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue'))) + }, { + name: 'guest.networks', + component: shallowRef(defineAsyncComponent(() => 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/views/AutogenView.vue b/ui/src/views/AutogenView.vue index 69eea6b6e4a..a03363a57f1 100644 --- a/ui/src/views/AutogenView.vue +++ b/ui/src/views/AutogenView.vue @@ -56,7 +56,7 @@ :value="$route.query.filter || (projectView && $route.name === 'vm' || ['Admin', 'DomainAdmin'].includes($store.getters.userInfo.roletype) && ['vm', 'iso', 'template'].includes($route.name) ? 'all' : ['publicip'].includes($route.name) - ? 'allocated': ['guestnetwork'].includes($route.name) ? 'all' : 'self')" + ? 'allocated' : ['guestnetwork', 'guestvlans'].includes($route.name) ? 'all' : 'self')" style="min-width: 100px; margin-left: 10px" @change="changeFilter" showSearch @@ -1497,9 +1497,9 @@ export default { query.isofilter = filter } else if (this.$route.name === 'guestnetwork') { if (filter === 'all') { - delete query.type + delete query.networkfilter } else { - query.type = filter + query.networkfilter = filter } } else if (this.$route.name === 'publicip') { query.state = filter @@ -1512,6 +1512,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 1f44215a524..85c9ae8a8da 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' @@ -108,9 +107,6 @@ export default { this.loading = false }) }, - isAdmin () { - return isAdmin() - }, handleRefresh () { }, refreshParent () { diff --git a/ui/src/views/network/CreateNetworkPermission.vue b/ui/src/views/network/CreateNetworkPermission.vue new file mode 100644 index 00000000000..88723463a4b --- /dev/null +++ b/ui/src/views/network/CreateNetworkPermission.vue @@ -0,0 +1,247 @@ +// 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/CreateSharedNetworkForm.vue b/ui/src/views/network/CreateSharedNetworkForm.vue index 0f97b393351..a8c7e9841f6 100644 --- a/ui/src/views/network/CreateSharedNetworkForm.vue +++ b/ui/src/views/network/CreateSharedNetworkForm.vue @@ -66,7 +66,7 @@ - + @@ -75,7 +75,7 @@ showSearch optionFilterProp="label" :filterOption="(input, option) => { - return option.children[0].children.toLowerCase().indexOf(input.toLowerCase()) >= 0 + return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 }" :loading="formPhysicalNetworkLoading" :placeholder="apiParams.physicalnetworkid.description" @@ -85,7 +85,7 @@ - + @@ -93,14 +93,14 @@ v-model:value="form.vlanid" :placeholder="apiParams.vlan.description"/> - +