From 294ff3d1772d932f6dad9fdb03e272b593b4aded Mon Sep 17 00:00:00 2001 From: Rakesh Venkatesh Date: Wed, 18 Dec 2019 16:38:49 +0100 Subject: [PATCH] New feature: Acquire specific public IP for network Currently in cloudstack, when we click on "Acquire New Ip", it will randomly acquire IP from the pool. With this enhancement, it is possible to select the IP from the drop down IP list of that network. Same thing applies for a VPC as well. --- .../com/cloud/network/NetworkService.java | 2 +- .../user/address/AssociateIPAddrCmd.java | 9 +- .../com/cloud/network/IpAddressManager.java | 2 +- .../network/element/CiscoVnmcElement.java | 2 +- .../cloud/network/IpAddressManagerImpl.java | 6 +- .../com/cloud/network/NetworkServiceImpl.java | 6 +- .../com/cloud/vpc/MockNetworkManagerImpl.java | 2 +- .../test_acquire_specified_public_ip.py | 486 ++++++++++++++++++ tools/marvin/marvin/lib/base.py | 11 +- ui/scripts/network.js | 49 ++ 10 files changed, 562 insertions(+), 13 deletions(-) create mode 100644 test/integration/component/test_acquire_specified_public_ip.py diff --git a/api/src/main/java/com/cloud/network/NetworkService.java b/api/src/main/java/com/cloud/network/NetworkService.java index b228c59e52ff..04f240b5e13e 100644 --- a/api/src/main/java/com/cloud/network/NetworkService.java +++ b/api/src/main/java/com/cloud/network/NetworkService.java @@ -56,7 +56,7 @@ public interface NetworkService { List getIsolatedNetworksOwnedByAccountInZone(long zoneId, Account owner); - IpAddress allocateIP(Account ipOwner, long zoneId, Long networkId, Boolean displayIp) throws ResourceAllocationException, InsufficientAddressCapacityException, + IpAddress allocateIP(Account ipOwner, long zoneId, Long networkId, Boolean displayIp, String ipaddress) throws ResourceAllocationException, InsufficientAddressCapacityException, ConcurrentOperationException; boolean releaseIpAddress(long ipAddressId) throws InsufficientAddressCapacityException; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java index 5b662b78237a..5beba26d0f53 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java @@ -125,6 +125,9 @@ public class AssociateIPAddrCmd extends BaseAsyncCreateCmd implements UserCmd { authorized = {RoleType.Admin}) private Boolean display; + @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, description="IP Address to be associated") + private String ipAddress; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -178,6 +181,10 @@ public Integer getRegionId() { return regionId; } + public String getIpAddress() { + return ipAddress; + } + public Long getNetworkId() { if (vpcId != null) { return null; @@ -306,7 +313,7 @@ public void create() throws ResourceAllocationException { IpAddress ip = null; if (!isPortable()) { - ip = _networkService.allocateIP(_accountService.getAccount(getEntityOwnerId()), getZoneId(), getNetworkId(), getDisplayIp()); + ip = _networkService.allocateIP(_accountService.getAccount(getEntityOwnerId()), getZoneId(), getNetworkId(), getDisplayIp(), ipAddress); } else { ip = _networkService.allocatePortableIP(_accountService.getAccount(getEntityOwnerId()), 1, getZoneId(), getNetworkId(), getVpcId()); } diff --git a/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java b/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java index a5512786bcd7..229248ac3f2a 100644 --- a/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java +++ b/engine/components-api/src/main/java/com/cloud/network/IpAddressManager.java @@ -169,7 +169,7 @@ void transferPortableIP(long ipAddrId, long currentNetworkId, long newNetworkId) PublicIp assignDedicateIpAddress(Account owner, Long guestNtwkId, Long vpcId, long dcId, boolean isSourceNat) throws ConcurrentOperationException, InsufficientAddressCapacityException; - IpAddress allocateIp(Account ipOwner, boolean isSystem, Account caller, long callerId, DataCenter zone, Boolean displayIp) + IpAddress allocateIp(Account ipOwner, boolean isSystem, Account caller, long callerId, DataCenter zone, Boolean displayIp, String ipaddress) throws ConcurrentOperationException, ResourceAllocationException, InsufficientAddressCapacityException; PublicIp assignPublicIpAddressFromVlans(long dcId, Long podId, Account owner, VlanType type, List vlanDbIds, Long networkId, String requestedIp, boolean isSystem) diff --git a/plugins/network-elements/cisco-vnmc/src/main/java/com/cloud/network/element/CiscoVnmcElement.java b/plugins/network-elements/cisco-vnmc/src/main/java/com/cloud/network/element/CiscoVnmcElement.java index 23c9c29b4b3e..ed650022d0cd 100644 --- a/plugins/network-elements/cisco-vnmc/src/main/java/com/cloud/network/element/CiscoVnmcElement.java +++ b/plugins/network-elements/cisco-vnmc/src/main/java/com/cloud/network/element/CiscoVnmcElement.java @@ -353,7 +353,7 @@ public boolean implement(final Network network, final NetworkOffering offering, try { Account caller = CallContext.current().getCallingAccount(); long callerUserId = CallContext.current().getCallingUserId(); - outsideIp = _ipAddrMgr.allocateIp(owner, false, caller, callerUserId, zone, true); + outsideIp = _ipAddrMgr.allocateIp(owner, false, caller, callerUserId, zone, true, null); } catch (ResourceAllocationException e) { s_logger.error("Unable to allocate additional public Ip address. Exception details " + e); throw new CloudRuntimeException("Unable to allocate additional public Ip address. Exception details " + e); diff --git a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java index 817efcccb5ed..55981e809547 100644 --- a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java +++ b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java @@ -444,7 +444,7 @@ private IpAddress allocateIP(Account ipOwner, boolean isSystem, long zoneId) thr DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId); - return allocateIp(ipOwner, isSystem, caller, callerUserId, zone, null); + return allocateIp(ipOwner, isSystem, caller, callerUserId, zone, null, null); } // An IP association is required in below cases @@ -1132,7 +1132,7 @@ public void releasePodIp(Long id) throws CloudRuntimeException { @DB @Override - public IpAddress allocateIp(final Account ipOwner, final boolean isSystem, Account caller, long callerUserId, final DataCenter zone, final Boolean displayIp) + public IpAddress allocateIp(final Account ipOwner, final boolean isSystem, Account caller, long callerUserId, final DataCenter zone, final Boolean displayIp, final String ipaddress) throws ConcurrentOperationException, ResourceAllocationException, InsufficientAddressCapacityException { @@ -1166,7 +1166,7 @@ public IpAddress allocateIp(final Account ipOwner, final boolean isSystem, Accou ip = Transaction.execute(new TransactionCallbackWithException() { @Override public PublicIp doInTransaction(TransactionStatus status) throws InsufficientAddressCapacityException { - PublicIp ip = fetchNewPublicIp(zone.getId(), null, null, ipOwner, vlanType, null, false, assign, null, isSystem, null, displayIp, false); + PublicIp ip = fetchNewPublicIp(zone.getId(), null, null, ipOwner, vlanType, null, false, assign, ipaddress, isSystem, null, displayIp, false); if (ip == null) { InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Unable to find available public IP addresses", DataCenter.class, zone diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java index f440ced13b20..1e9eb2d325f5 100644 --- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java @@ -520,7 +520,7 @@ public List getIsolatedNetworksWithSourceNATOwnedByAccountInZ @Override @ActionEvent(eventType = EventTypes.EVENT_NET_IP_ASSIGN, eventDescription = "allocating Ip", create = true) - public IpAddress allocateIP(Account ipOwner, long zoneId, Long networkId, Boolean displayIp) + public IpAddress allocateIP(Account ipOwner, long zoneId, Long networkId, Boolean displayIp, String ipaddress) throws ResourceAllocationException, InsufficientAddressCapacityException, ConcurrentOperationException { Account caller = CallContext.current().getCallingAccount(); @@ -544,7 +544,7 @@ public IpAddress allocateIP(Account ipOwner, long zoneId, Long networkId, Boolea if (s_logger.isDebugEnabled()) { s_logger.debug("Associate IP address called by the user " + callerUserId + " account " + ipOwner.getId()); } - return _ipAddrMgr.allocateIp(ipOwner, false, caller, callerUserId, zone, displayIp); + return _ipAddrMgr.allocateIp(ipOwner, false, caller, callerUserId, zone, displayIp, ipaddress); } else { throw new InvalidParameterValueException("Associate IP address can only be called on the shared networks in the advanced zone" + " with Firewall/Source Nat/Static Nat/Port Forwarding/Load balancing services enabled"); @@ -555,7 +555,7 @@ public IpAddress allocateIP(Account ipOwner, long zoneId, Long networkId, Boolea _accountMgr.checkAccess(caller, null, false, ipOwner); } - return _ipAddrMgr.allocateIp(ipOwner, false, caller, callerUserId, zone, displayIp); + return _ipAddrMgr.allocateIp(ipOwner, false, caller, callerUserId, zone, displayIp, ipaddress); } @Override diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java index e99c2a864cb6..09fd997f6184 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java @@ -160,7 +160,7 @@ public List getIsolatedNetworksOwnedByAccountInZone(long zone * @see com.cloud.network.NetworkService#allocateIP(com.cloud.user.Account, long, java.lang.Long) */ @Override - public IpAddress allocateIP(Account ipOwner, long zoneId, Long networkId, Boolean displayIp) throws ResourceAllocationException, InsufficientAddressCapacityException, + public IpAddress allocateIP(Account ipOwner, long zoneId, Long networkId, Boolean displayIp, String ipaddress) throws ResourceAllocationException, InsufficientAddressCapacityException, ConcurrentOperationException { // TODO Auto-generated method stub return null; diff --git a/test/integration/component/test_acquire_specified_public_ip.py b/test/integration/component/test_acquire_specified_public_ip.py new file mode 100644 index 000000000000..01a4470f161d --- /dev/null +++ b/test/integration/component/test_acquire_specified_public_ip.py @@ -0,0 +1,486 @@ +# 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 acquiring a specified public IP for isolated network or vpc +""" + +# Import Local Modules +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.cloudstackAPI import createVlanIpRange +from marvin.lib.utils import (validateList, + cleanup_resources) +from marvin.lib.base import (Account, + Domain, + Configurations, + VirtualMachine, + ServiceOffering, + VpcOffering, + Zone, + Network, + VPC, + PublicIPAddress, + PublicIpRange) +from marvin.lib.common import (get_domain, + get_zone, + get_free_vlan, + get_template) +import logging +import random + +class TestAcquireSpecifiedPublicIp(cloudstackTestCase): + @classmethod + def setUpClass(cls): + cls.testClient = super( + TestAcquireSpecifiedPublicIp, + 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 = [] + + if str(cls.zone.securitygroupsenabled) == "True": + sys.exit(1) + + cls.logger = logging.getLogger("TestAcquireSpecifiedPublicIp") + cls.stream_handler = logging.StreamHandler() + cls.logger.setLevel(logging.DEBUG) + cls.logger.addHandler(cls.stream_handler) + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.apiclient) + + # Create new domain1 + cls.domain1 = Domain.create( + cls.apiclient, + services=cls.services["acl"]["domain1"], + parentdomainid=cls.domain.id) + + # Create account1 + cls.account1 = Account.create( + cls.apiclient, + cls.services["acl"]["accountD1"], + domainid=cls.domain1.id + ) + + # Create domain2 + cls.domain2 = Domain.create( + cls.apiclient, + services=cls.services["acl"]["domain2"], + parentdomainid=cls.domain.id) + + # Create account2 + cls.account2 = Account.create( + cls.apiclient, + cls.services["acl"]["accountD2"], + domainid=cls.domain2.id + ) + + cls.services["publiciprange"]["zoneid"] = cls.zone.id + cls.services["publiciprange"]["forvirtualnetwork"] = "true" + + # Create public ip range 1 + cls.services["publiciprange"]["vlan"] = get_free_vlan( + cls.apiclient, + cls.zone.id)[1] + random_subnet_number = random.randrange(10,20) + cls.services["publiciprange"]["gateway"] = "172.16." + \ + str(random_subnet_number) + ".1" + cls.services["publiciprange"]["startip"] = "172.16." + \ + str(random_subnet_number) + ".2" + cls.services["publiciprange"]["endip"] = "172.16." + \ + str(random_subnet_number) + ".10" + cls.services["publiciprange"]["netmask"] = "255.255.255.0" + cls.public_ip_range1 = PublicIpRange.create( + cls.apiclient, + cls.services["publiciprange"] + ) + PublicIpRange.dedicate( + cls.apiclient, + cls.public_ip_range1.vlan.id, + domainid=cls.account1.domainid + ) + + # Create public ip range 2 + cls.services["publiciprange"]["vlan"] = get_free_vlan( + cls.apiclient, + cls.zone.id)[1] + cls.services["publiciprange"]["gateway"] = "172.16." + \ + str(random_subnet_number + 1) + ".1" + cls.services["publiciprange"]["startip"] = "172.16." + \ + str(random_subnet_number + 1) + ".2" + cls.services["publiciprange"]["endip"] = "172.16." + \ + str(random_subnet_number + 1) + ".10" + cls.services["publiciprange"]["netmask"] = "255.255.255.0" + cls.public_ip_range2 = PublicIpRange.create( + cls.apiclient, + cls.services["publiciprange"] + ) + PublicIpRange.dedicate( + cls.apiclient, + cls.public_ip_range2.vlan.id, + account=cls.account1.name, + domainid=cls.account1.domainid + ) + + # Create public ip range 3 + cls.services["publiciprange"]["vlan"] = get_free_vlan( + cls.apiclient, + cls.zone.id)[1] + cls.services["publiciprange"]["gateway"] = "172.16." + \ + str(random_subnet_number + 2) + ".1" + cls.services["publiciprange"]["startip"] = "172.16." + \ + str(random_subnet_number + 2) + ".2" + cls.services["publiciprange"]["endip"] = "172.16." + \ + str(random_subnet_number + 2) + ".10" + cls.services["publiciprange"]["netmask"] = "255.255.255.0" + cls.public_ip_range3 = PublicIpRange.create( + cls.apiclient, + cls.services["publiciprange"] + ) + PublicIpRange.dedicate( + cls.apiclient, + cls.public_ip_range3.vlan.id, + domainid=cls.account2.domainid + ) + + # Create public ip range 4 + cls.services["publiciprange"]["vlan"] = get_free_vlan( + cls.apiclient, + cls.zone.id)[1] + cls.services["publiciprange"]["gateway"] = "172.16." + \ + str(random_subnet_number + 3) + ".1" + cls.services["publiciprange"]["startip"] = "172.16." + \ + str(random_subnet_number + 3) + ".2" + cls.services["publiciprange"]["endip"] = "172.16." + \ + str(random_subnet_number + 3) + ".10" + cls.services["publiciprange"]["netmask"] = "255.255.255.0" + cls.public_ip_range4 = PublicIpRange.create( + cls.apiclient, + cls.services["publiciprange"] + ) + PublicIpRange.dedicate( + cls.apiclient, + cls.public_ip_range4.vlan.id, + account=cls.account2.name, + domainid=cls.account2.domainid + ) + + # Create public ip range 5 + cls.services["publiciprange"]["vlan"] = get_free_vlan( + cls.apiclient, + cls.zone.id)[1] + cls.services["publiciprange"]["gateway"] = "172.16." + \ + str(random_subnet_number + 4) + ".1" + cls.services["publiciprange"]["startip"] = "172.16." + \ + str(random_subnet_number + 4) + ".2" + cls.services["publiciprange"]["endip"] = "172.16." + \ + str(random_subnet_number + 4) + ".10" + cls.services["publiciprange"]["netmask"] = "255.255.255.0" + cls.public_ip_range5 = PublicIpRange.create( + cls.apiclient, + cls.services["publiciprange"] + ) + + cls._cleanup.append(cls.account1) + cls._cleanup.append(cls.domain1) + cls._cleanup.append(cls.account2) + cls._cleanup.append(cls.domain2) + cls._cleanup.append(cls.public_ip_range1) + cls._cleanup.append(cls.public_ip_range2) + cls._cleanup.append(cls.public_ip_range3) + cls._cleanup.append(cls.public_ip_range4) + cls._cleanup.append(cls.public_ip_range5) + + @classmethod + def tearDownClass(cls): + try: + cleanup_resources(cls.apiclient, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(cls): + cls.apiclient = cls.testClient.getApiClient() + cls.cleanup = [] + return + + def tearDown(cls): + try: + cleanup_resources(cls.apiclient, cls.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["advanced"], required_hardware="false") + def test_01_acquire_public_ip_in_isolated_network(self): + # Validate the following + # 1. create a vm . it will create a network as well. + # 2. assign a specified IP from subnet which is dedicated to domain1, it should succeed + # 3. assign a specified IP from subnet which is dedicated to domain1/account1, it should succeed + # 4. assign a specified IP from subnet which is dedicated to domain2, it should fail + # 5. assign a specified IP from subnet which is dedicated to domain2/account2, it should fail + # 6. update account setting use.system.public.ips to false, assign a specified IP from subnet which is public, it should fail + # 7. update account setting use.system.public.ips to true, assign a specified IP from subnet which is public, it should succeed + + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + self.cleanup.append(self.service_offering) + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["small"], + templateid=self.template.id, + accountid=self.account1.name, + domainid=self.account1.domainid, + serviceofferingid=self.service_offering.id, + zoneid=self.zone.id + ) + + networks = Network.list( + self.apiclient, + account=self.account1.name, + domainid=self.account1.domainid, + listall=True + ) + self.assertEqual( + isinstance(networks, list), + True, + "List networks should return a valid response for created network" + ) + network = networks[0] + + # Associate IP in range dedicated to domain1 + ip_address_1 = self.get_free_ipaddress(self.public_ip_range1.vlan.id) + ipaddress = PublicIPAddress.create( + self.apiclient, + zoneid=self.zone.id, + networkid=network.id, + ipaddress=ip_address_1 + ) + self.assertIsNotNone( + ipaddress, + "Failed to Associate IP Address" + ) + self.cleanup.append(ipaddress) + self.assertEqual(ipaddress.ipaddress.ipaddress, ip_address_1, "Associated IP is not same as specified") + + # Associate IP in range dedicated to domain1/account1 + ip_address_2 = self.get_free_ipaddress(self.public_ip_range2.vlan.id) + ipaddress = PublicIPAddress.create( + self.apiclient, + zoneid=self.zone.id, + networkid=network.id, + ipaddress=ip_address_2 + ) + self.assertIsNotNone( + ipaddress, + "Failed to Associate IP Address" + ) + self.cleanup.append(ipaddress) + self.assertEqual(ipaddress.ipaddress.ipaddress, ip_address_2, "Associated IP is not same as specified") + + # Associate IP in range dedicated to domain2 + ip_address_3 = self.get_free_ipaddress(self.public_ip_range3.vlan.id) + with self.assertRaises(Exception): + ipaddress = PublicIPAddress.create( + self.apiclient, + zoneid=self.zone.id, + networkid=network.id, + ipaddress=ip_address_3 + ) + + # Associate IP in range dedicated to domain2/account2 + ip_address_4 = self.get_free_ipaddress(self.public_ip_range4.vlan.id) + with self.assertRaises(Exception): + ipaddress = PublicIPAddress.create( + self.apiclient, + zoneid=self.zone.id, + networkid=network.id, + ipaddress=ip_address_4 + ) + + # Associate IP in public IP pool + Configurations.update( + self.apiclient, + name="use.system.public.ips", + value="false", + accountid=self.account1.id + ) + + ip_address_5 = self.get_free_ipaddress(self.public_ip_range5.vlan.id) + with self.assertRaises(Exception): + ipaddress = PublicIPAddress.create( + self.apiclient, + zoneid=self.zone.id, + networkid=network.id, + ipaddress=ip_address_5 + ) + + Configurations.update( + self.apiclient, + name="use.system.public.ips", + value="true", + accountid=self.account1.id + ) + ipaddress = PublicIPAddress.create( + self.apiclient, + zoneid=self.zone.id, + networkid=network.id, + ipaddress=ip_address_5 + ) + self.assertIsNotNone( + ipaddress, + "Failed to Associate IP Address" + ) + self.cleanup.append(ipaddress) + self.assertEqual(ipaddress.ipaddress.ipaddress, ip_address_5, "Associated IP is not same as specified") + + self.cleanup.append(self.virtual_machine) + self.cleanup.append(self.service_offering) + + return + + @attr(tags=["advanced"], required_hardware="false") + def test_02_acquire_public_ip_in_vpc(self): + # Validate the following + # 1. create a VPC with default offering. + # 2. assign a specified IP from subnet which is dedicated to domain1, it should succeed + # 3. assign a specified IP from subnet which is dedicated to domain1/account1, it should succeed + # 4. assign a specified IP from subnet which is dedicated to domain2, it should fail + # 5. assign a specified IP from subnet which is dedicated to domain2/account2, it should fail + # 6. update account setting use.system.public.ips to false, assign a specified IP from subnet which is public, it should fail + # 7. update account setting use.system.public.ips to true, assign a specified IP from subnet which is public, it should succeed + + vpcOffering = VpcOffering.list(self.apiclient, name="Default VPC offering") + vpc = VPC.create( + apiclient=self.apiclient, + services=self.services["vpc"], + vpcofferingid=vpcOffering[0].id, + zoneid=self.zone.id, + account=self.account1.name, + domainid=self.account1.domainid + ) + + # Associate IP in range dedicated to domain1 + ip_address_1 = self.get_free_ipaddress(self.public_ip_range1.vlan.id) + ipaddress = PublicIPAddress.create( + self.apiclient, + zoneid=self.zone.id, + vpcid=vpc.id, + ipaddress=ip_address_1 + ) + self.assertIsNotNone( + ipaddress, + "Failed to Associate IP Address" + ) + self.cleanup.append(ipaddress) + self.assertEqual(ipaddress.ipaddress.ipaddress, ip_address_1, "Associated IP is not same as specified") + + # Associate IP in range dedicated to domain1/account1 + ip_address_2 = self.get_free_ipaddress(self.public_ip_range2.vlan.id) + ipaddress = PublicIPAddress.create( + self.apiclient, + zoneid=self.zone.id, + vpcid=vpc.id, + ipaddress=ip_address_2 + ) + self.assertIsNotNone( + ipaddress, + "Failed to Associate IP Address" + ) + self.cleanup.append(ipaddress) + self.assertEqual(ipaddress.ipaddress.ipaddress, ip_address_2, "Associated IP is not same as specified") + + # Associate IP in range dedicated to domain2 + ip_address_3 = self.get_free_ipaddress(self.public_ip_range3.vlan.id) + with self.assertRaises(Exception): + ipaddress = PublicIPAddress.create( + self.apiclient, + zoneid=self.zone.id, + vpcid=vpc.id, + ipaddress=ip_address_3 + ) + + # Associate IP in range dedicated to domain2/account2 + ip_address_4 = self.get_free_ipaddress(self.public_ip_range4.vlan.id) + with self.assertRaises(Exception): + ipaddress = PublicIPAddress.create( + self.apiclient, + zoneid=self.zone.id, + vpcid=vpc.id, + ipaddress=ip_address_4 + ) + + # Associate IP in public IP pool + Configurations.update( + self.apiclient, + name="use.system.public.ips", + value="false", + accountid=self.account1.id + ) + + ip_address_5 = self.get_free_ipaddress(self.public_ip_range5.vlan.id) + with self.assertRaises(Exception): + ipaddress = PublicIPAddress.create( + self.apiclient, + zoneid=self.zone.id, + vpcid=vpc.id, + ipaddress=ip_address_5 + ) + + Configurations.update( + self.apiclient, + name="use.system.public.ips", + value="true", + accountid=self.account1.id + ) + ipaddress = PublicIPAddress.create( + self.apiclient, + zoneid=self.zone.id, + vpcid=vpc.id, + ipaddress=ip_address_5 + ) + self.assertIsNotNone( + ipaddress, + "Failed to Associate IP Address" + ) + self.cleanup.append(ipaddress) + self.assertEqual(ipaddress.ipaddress.ipaddress, ip_address_5, "Associated IP is not same as specified") + + self.cleanup.append(vpc) + return + + def get_free_ipaddress(self, vlanId): + ipaddresses = PublicIPAddress.list( + self.apiclient, + vlanid=vlanId, + state='Free' + ) + self.assertEqual( + isinstance(ipaddresses, list), + True, + "List ipaddresses should return a valid response for Free ipaddresses" + ) + random.shuffle(ipaddresses) + return ipaddresses[0].ipaddress diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index e7572e918015..409fd325c3e9 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -1667,7 +1667,7 @@ def __init__(self, items): @classmethod def create(cls, apiclient, accountid=None, zoneid=None, domainid=None, services=None, networkid=None, projectid=None, vpcid=None, - isportable=False): + isportable=False, ipaddress=None): """Associate Public IP address""" cmd = associateIpAddress.associateIpAddressCmd() @@ -1697,6 +1697,9 @@ def create(cls, apiclient, accountid=None, zoneid=None, domainid=None, if vpcid: cmd.vpcid = vpcid + + if ipaddress: + cmd.ipaddress = ipaddress return PublicIPAddress(apiclient.associateIpAddress(cmd).__dict__) def delete(self, apiclient): @@ -4054,7 +4057,7 @@ class Configurations: """Manage Configuration""" @classmethod - def update(cls, apiclient, name, value=None, zoneid=None, clusterid=None, storageid=None): + def update(cls, apiclient, name, value=None, zoneid=None, clusterid=None, storageid=None, domainid=None, accountid=None): """Updates the specified configuration""" cmd = updateConfiguration.updateConfigurationCmd() @@ -4067,6 +4070,10 @@ def update(cls, apiclient, name, value=None, zoneid=None, clusterid=None, storag cmd.clusterid = clusterid if storageid: cmd.storageid = storageid + if domainid: + cmd.domainid = domainid + if accountid: + cmd.accountid = accountid apiclient.updateConfiguration(cmd) @classmethod diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 7e5f554d08a5..e1d248477ba2 100644 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -2121,6 +2121,50 @@ }); }, isHidden: true + }, + ipaddress: { + label: 'label.ip.address', + select: function(args) { + var data = { + forvirtualnetwork : true, + allocatedonly: false + }; + if ('vpc' in args.context) { //from VPC section + $.extend(data, { + zoneid: args.context.vpc[0].zoneid, + domainid: args.context.vpc[0].domainid, + account: args.context.vpc[0].account + }); + } else if ('networks' in args.context) { //from Guest Network section + $.extend(data, { + zoneid: args.context.networks[0].zoneid, + domainid: args.context.networks[0].domainid, + account: args.context.networks[0].account + }); + } + $.ajax({ + url: createURL('listPublicIpAddresses'), + data: data, + success: function(json) { + var ips = json.listpublicipaddressesresponse.publicipaddress; + var items = [{ + id: -1, + description: '' + }]; + $(ips).each(function() { + if (this.state == "Free") { + items.push({ + id: this.ipaddress, + description: this.ipaddress + }); + } + }); + args.response.success({ + data: items + }); + } + }) + } } } }, @@ -2149,6 +2193,11 @@ } } + if (args.data.ipaddress != null && args.data.ipaddress.length > 0) { + $.extend(dataObj, { + ipaddress: args.data.ipaddress + }); + } $.ajax({ url: createURL('associateIpAddress'), data: dataObj,