diff --git a/api/src/main/java/com/cloud/network/NetworkService.java b/api/src/main/java/com/cloud/network/NetworkService.java index b228c59e52f..04f240b5e13 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 5b662b78237..5beba26d0f5 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 class AssociateIPAddrCmd extends BaseAsyncCreateCmd implements UserCmd { return regionId; } + public String getIpAddress() { + return ipAddress; + } + public Long getNetworkId() { if (vpcId != null) { return null; @@ -306,7 +313,7 @@ public class AssociateIPAddrCmd extends BaseAsyncCreateCmd implements UserCmd { 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 a5512786bcd..229248ac3f2 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 @@ public interface IpAddressManager { 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 23c9c29b4b3..ed650022d0c 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 class CiscoVnmcElement extends AdapterBase implements SourceNatServicePro 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 817efcccb5e..55981e80954 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 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage 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 class IpAddressManagerImpl extends ManagerBase implements IpAddressManage @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 class IpAddressManagerImpl extends ManagerBase implements IpAddressManage 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 f440ced13b2..1e9eb2d325f 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 class NetworkServiceImpl extends ManagerBase implements NetworkService { @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 class NetworkServiceImpl extends ManagerBase implements NetworkService { 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 class NetworkServiceImpl extends ManagerBase implements NetworkService { _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 e99c2a864cb..09fd997f618 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 class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches * @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 00000000000..01a4470f161 --- /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 e7572e91801..409fd325c3e 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -1667,7 +1667,7 @@ class PublicIPAddress: @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 @@ class PublicIPAddress: 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 @@ class Configurations: 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 aa6af96afd4..86f9fa105e5 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,