diff --git a/api/src/com/cloud/configuration/ConfigurationService.java b/api/src/com/cloud/configuration/ConfigurationService.java index ecea00018e1..b13122208ee 100644 --- a/api/src/com/cloud/configuration/ConfigurationService.java +++ b/api/src/com/cloud/configuration/ConfigurationService.java @@ -48,6 +48,7 @@ import org.apache.cloudstack.region.PortableIpRange; import com.cloud.dc.DataCenter; import com.cloud.dc.Pod; import com.cloud.dc.Vlan; +import com.cloud.domain.Domain; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; @@ -255,6 +256,8 @@ public interface ConfigurationService { Account getVlanAccount(long vlanId); + Domain getVlanDomain(long vlanId); + List listNetworkOfferings(TrafficType trafficType, boolean systemOnly); Long getDefaultPageSize(); diff --git a/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java index 2cba8e69e45..58a8458312d 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/vlan/DedicatePublicIpRangeCmd.java @@ -47,7 +47,7 @@ public class DedicatePublicIpRangeCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VlanIpRangeResponse.class, required = true, description = "the id of the VLAN IP range") private Long id; - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = true, description = "account who will own the VLAN") + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account who will own the VLAN") private String accountName; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "project who will own the VLAN") diff --git a/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java b/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java index 13ca3f9bf0f..ec3df0e7abc 100644 --- a/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java +++ b/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java @@ -26,6 +26,7 @@ import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.Pod; import com.cloud.dc.Vlan; +import com.cloud.domain.Domain; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; @@ -213,7 +214,7 @@ public interface ConfigurationManager { Map details, boolean egressDefaultPolicy, Integer maxconn, boolean enableKeepAlive); Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, Long podId, String startIP, String endIP, - String vlanGateway, String vlanNetmask, String vlanId, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr) + String vlanGateway, String vlanNetmask, String vlanId, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr) throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException; void createDefaultSystemNetworks(long zoneId) throws ConcurrentOperationException; diff --git a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 13f35196a5a..67ed2a7d255 100644 --- a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -140,6 +140,7 @@ + diff --git a/engine/schema/src/com/cloud/dc/DomainVlanMapVO.java b/engine/schema/src/com/cloud/dc/DomainVlanMapVO.java new file mode 100644 index 00000000000..22d39dc16c9 --- /dev/null +++ b/engine/schema/src/com/cloud/dc/DomainVlanMapVO.java @@ -0,0 +1,63 @@ +// 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.dc; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name="domain_vlan_map") +public class DomainVlanMapVO implements InternalIdentity { + + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private long id; + + @Column(name="domain_id") + private long domainId; + + @Column(name="vlan_db_id") + private long vlanDbId; + + public DomainVlanMapVO(long domainId, long vlanDbId) { + this.domainId = domainId; + this.vlanDbId = vlanDbId; + } + + public DomainVlanMapVO() { + + } + + public long getId() { + return id; + } + + public long getDomainId() { + return domainId; + } + + public long getVlanDbId() { + return vlanDbId; + } +} diff --git a/engine/schema/src/com/cloud/dc/dao/DomainVlanMapDao.java b/engine/schema/src/com/cloud/dc/dao/DomainVlanMapDao.java new file mode 100644 index 00000000000..6af16bbace9 --- /dev/null +++ b/engine/schema/src/com/cloud/dc/dao/DomainVlanMapDao.java @@ -0,0 +1,28 @@ +// 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.dc.dao; + +import java.util.List; + +import com.cloud.dc.DomainVlanMapVO; +import com.cloud.utils.db.GenericDao; + +public interface DomainVlanMapDao extends GenericDao { + public List listDomainVlanMapsByDomain(long domainId); + public List listDomainVlanMapsByVlan(long vlanDbId); + public DomainVlanMapVO findDomainVlanMap(long domainId, long vlanDbId); +} diff --git a/engine/schema/src/com/cloud/dc/dao/DomainVlanMapDaoImpl.java b/engine/schema/src/com/cloud/dc/dao/DomainVlanMapDaoImpl.java new file mode 100644 index 00000000000..a5fd5fa1300 --- /dev/null +++ b/engine/schema/src/com/cloud/dc/dao/DomainVlanMapDaoImpl.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 com.cloud.dc.dao; + +import java.util.List; + +import javax.ejb.Local; + +import org.springframework.stereotype.Component; + +import com.cloud.dc.DomainVlanMapVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Component +@Local(value={DomainVlanMapDao.class}) +public class DomainVlanMapDaoImpl extends GenericDaoBase implements DomainVlanMapDao { + protected SearchBuilder DomainSearch; + protected SearchBuilder VlanSearch; + protected SearchBuilder DomainVlanSearch; + + @Override + public List listDomainVlanMapsByDomain(long domainId) { + SearchCriteria sc = DomainSearch.create(); + sc.setParameters("domainId", domainId); + return listIncludingRemovedBy(sc); + } + + @Override + public List listDomainVlanMapsByVlan(long vlanDbId) { + SearchCriteria sc = VlanSearch.create(); + sc.setParameters("vlanDbId", vlanDbId); + return listIncludingRemovedBy(sc); + } + + @Override + public DomainVlanMapVO findDomainVlanMap(long domainId, long vlanDbId) { + SearchCriteria sc = DomainVlanSearch.create(); + sc.setParameters("domainId", domainId); + sc.setParameters("vlanDbId", vlanDbId); + return findOneIncludingRemovedBy(sc); + } + + public DomainVlanMapDaoImpl() { + DomainSearch = createSearchBuilder(); + DomainSearch.and("domainId", DomainSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + DomainSearch.done(); + + VlanSearch = createSearchBuilder(); + VlanSearch.and("vlanDbId", VlanSearch.entity().getVlanDbId(), SearchCriteria.Op.EQ); + VlanSearch.done(); + + DomainVlanSearch = createSearchBuilder(); + DomainVlanSearch.and("domainId", DomainVlanSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + DomainVlanSearch.and("vlanDbId", DomainVlanSearch.entity().getVlanDbId(), SearchCriteria.Op.EQ); + DomainVlanSearch.done(); + } + +} \ No newline at end of file diff --git a/engine/schema/src/com/cloud/dc/dao/VlanDaoImpl.java b/engine/schema/src/com/cloud/dc/dao/VlanDaoImpl.java index 165ef219c4e..eabfbaa8b28 100644 --- a/engine/schema/src/com/cloud/dc/dao/VlanDaoImpl.java +++ b/engine/schema/src/com/cloud/dc/dao/VlanDaoImpl.java @@ -29,6 +29,7 @@ import javax.naming.ConfigurationException; import org.springframework.stereotype.Component; import com.cloud.dc.AccountVlanMapVO; +import com.cloud.dc.DomainVlanMapVO; import com.cloud.dc.PodVlanMapVO; import com.cloud.dc.Vlan; import com.cloud.dc.Vlan.VlanType; @@ -62,12 +63,15 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao protected SearchBuilder DedicatedVlanSearch; protected SearchBuilder AccountVlanMapSearch; + protected SearchBuilder DomainVlanMapSearch; @Inject protected PodVlanMapDao _podVlanMapDao; @Inject protected AccountVlanMapDao _accountVlanMapDao; @Inject + protected DomainVlanMapDao _domainVlanMapDao; + @Inject protected IPAddressDao _ipAddressDao; @Override @@ -214,8 +218,12 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao AccountVlanMapSearch.and("accountId", AccountVlanMapSearch.entity().getAccountId(), SearchCriteria.Op.NULL); ZoneWideNonDedicatedVlanSearch.join("AccountVlanMapSearch", AccountVlanMapSearch, ZoneWideNonDedicatedVlanSearch.entity().getId(), AccountVlanMapSearch.entity() .getVlanDbId(), JoinBuilder.JoinType.LEFTOUTER); + DomainVlanMapSearch = _domainVlanMapDao.createSearchBuilder(); + DomainVlanMapSearch.and("domainId", DomainVlanMapSearch.entity().getDomainId(), SearchCriteria.Op.NULL); + ZoneWideNonDedicatedVlanSearch.join("DomainVlanMapSearch", DomainVlanMapSearch, ZoneWideNonDedicatedVlanSearch.entity().getId(), DomainVlanMapSearch.entity().getVlanDbId(), JoinBuilder.JoinType.LEFTOUTER); ZoneWideNonDedicatedVlanSearch.done(); AccountVlanMapSearch.done(); + DomainVlanMapSearch.done(); DedicatedVlanSearch = createSearchBuilder(); AccountVlanMapSearch = _accountVlanMapDao.createSearchBuilder(); diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java index 1da62ccb833..1c55c674e64 100644 --- a/server/src/com/cloud/api/ApiDBUtils.java +++ b/server/src/com/cloud/api/ApiDBUtils.java @@ -139,6 +139,7 @@ import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.dc.dao.VlanDao; +import com.cloud.domain.Domain; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; import com.cloud.event.Event; @@ -1245,6 +1246,10 @@ public class ApiDBUtils { return s_configSvc.getVlanAccount(vlanId); } + public static Domain getVlanDomain(long vlanId) { + return s_configSvc.getVlanDomain(vlanId); + } + public static boolean isSecurityGroupEnabledInZone(long zoneId) { DataCenterVO dc = s_zoneDao.findById(zoneId); if (dc == null) { diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 00f1a277c4a..1b72919d809 100644 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -666,6 +666,21 @@ public class ApiResponseHelper implements ResponseGenerator { if (owner != null) { populateAccount(vlanResponse, owner.getId()); populateDomain(vlanResponse, owner.getDomainId()); + } else { + Domain domain = ApiDBUtils.getVlanDomain(vlan.getId()); + if (domain != null) { + populateDomain(vlanResponse, domain.getId()); + } else { + Long networkId = vlan.getNetworkId(); + if (networkId != null) { + Network network = _ntwkModel.getNetwork(networkId); + if (network != null) { + Long accountId = network.getAccountId(); + populateAccount(vlanResponse, accountId); + populateDomain(vlanResponse, ApiDBUtils.findAccountById(accountId).getDomainId()); + } + } + } } if (vlan.getPhysicalNetworkId() != null) { diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 6f4f476a542..9032ff959a5 100644 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -100,6 +100,7 @@ import com.cloud.dc.DataCenterIpAddressVO; import com.cloud.dc.DataCenterLinkLocalIpAddressVO; import com.cloud.dc.DataCenterVO; import com.cloud.dc.DedicatedResourceVO; +import com.cloud.dc.DomainVlanMapVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.Pod; import com.cloud.dc.PodVlanMapVO; @@ -113,6 +114,7 @@ import com.cloud.dc.dao.DataCenterDetailsDao; import com.cloud.dc.dao.DataCenterIpAddressDao; import com.cloud.dc.dao.DataCenterLinkLocalIpAddressDao; import com.cloud.dc.dao.DedicatedResourceDao; +import com.cloud.dc.dao.DomainVlanMapDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.dc.dao.PodVlanMapDao; import com.cloud.dc.dao.VlanDao; @@ -237,6 +239,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Inject AccountVlanMapDao _accountVlanMapDao; @Inject + DomainVlanMapDao _domainVlanMapDao; + @Inject PodVlanMapDao _podVlanMapDao; @Inject DataCenterDao _zoneDao; @@ -2621,8 +2625,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } vlanOwner = _accountMgr.getAccount(project.getProjectAccountId()); + if (vlanOwner == null) { + throw new InvalidParameterValueException("Please specify a valid projectId"); + } } + Domain domain = null; if (accountName != null && domainId != null) { vlanOwner = _accountDao.findActiveAccount(accountName, domainId); if (vlanOwner == null) { @@ -2631,6 +2639,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // by default vlan is dedicated to system account vlanOwner = null; } + } else if (domainId != null) { + domain = _domainDao.findById(domainId); + if (domain == null) { + throw new InvalidParameterValueException("Please specify a valid domain id"); + } } // Verify that network exists @@ -2789,12 +2802,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } return commitVlan(zoneId, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId, forVirtualNetwork, networkId, physicalNetworkId, startIPv6, endIPv6, ip6Gateway, - ip6Cidr, vlanOwner, network, sameSubnet); + ip6Cidr, domain, vlanOwner, network, sameSubnet); } private Vlan commitVlan(final Long zoneId, final Long podId, final String startIP, final String endIP, final String newVlanGatewayFinal, final String newVlanNetmaskFinal, final String vlanId, final Boolean forVirtualNetwork, final Long networkId, final Long physicalNetworkId, final String startIPv6, final String endIPv6, - final String ip6Gateway, final String ip6Cidr, final Account vlanOwner, final Network network, final Pair> sameSubnet) { + final String ip6Gateway, final String ip6Cidr, final Domain domain, final Account vlanOwner, final Network network, final Pair> sameSubnet) { return Transaction.execute(new TransactionCallback() { @Override public Vlan doInTransaction(final TransactionStatus status) { @@ -2818,7 +2831,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati newVlanNetmask = sameSubnet.second().second(); } final Vlan vlan = createVlanAndPublicIpRange(zoneId, networkId, physicalNetworkId, forVirtualNetwork, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId, - vlanOwner, startIPv6, endIPv6, ip6Gateway, ip6Cidr); + domain, vlanOwner, startIPv6, endIPv6, ip6Gateway, ip6Cidr); // create an entry in the nic_secondary table. This will be the new // gateway that will be configured on the corresponding routervm. return vlan; @@ -2925,7 +2938,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @DB public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, final long physicalNetworkId, final boolean forVirtualNetwork, final Long podId, final String startIP, final String endIP, - final String vlanGateway, final String vlanNetmask, String vlanId, final Account vlanOwner, final String startIPv6, final String endIPv6, final String vlanIp6Gateway, final String vlanIp6Cidr) { + final String vlanGateway, final String vlanNetmask, String vlanId, Domain domain, final Account vlanOwner, final String startIPv6, final String endIPv6, final String vlanIp6Gateway, final String vlanIp6Cidr) { final Network network = _networkModel.getNetwork(networkId); boolean ipv4 = false, ipv6 = false; @@ -3003,7 +3016,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati final VlanType vlanType = forVirtualNetwork ? VlanType.VirtualNetwork : VlanType.DirectAttached; - if (vlanOwner != null && zone.getNetworkType() != NetworkType.Advanced) { + if ((domain != null || vlanOwner != null) && zone.getNetworkType() != NetworkType.Advanced) { throw new InvalidParameterValueException("Vlan owner can be defined only in the zone of type " + NetworkType.Advanced); } @@ -3157,14 +3170,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } // Everything was fine, so persist the VLAN - final VlanVO vlan = commitVlanAndIpRange(zoneId, networkId, physicalNetworkId, podId, startIP, endIP, vlanGateway, vlanNetmask, vlanId, vlanOwner, vlanIp6Gateway, vlanIp6Cidr, + final VlanVO vlan = commitVlanAndIpRange(zoneId, networkId, physicalNetworkId, podId, startIP, endIP, vlanGateway, vlanNetmask, vlanId, domain, vlanOwner, vlanIp6Gateway, vlanIp6Cidr, ipv4, zone, vlanType, ipv6Range, ipRange); return vlan; } 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 Account vlanOwner, final String vlanIp6Gateway, final String vlanIp6Cidr, + 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) { return Transaction.execute(new TransactionCallback() { @Override @@ -3196,6 +3209,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } // increment resource count for dedicated public ip's _resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, new Long(ips.size())); + } else if (domain != null) { + // This VLAN is domain-wide, so create a DomainVlanMapVO entry + final DomainVlanMapVO domainVlanMapVO = new DomainVlanMapVO(domain.getId(), vlan.getId()); + _domainVlanMapDao.persist(domainVlanMapVO); } else if (podId != null) { // This VLAN is pod-wide, so create a PodVlanMapVO entry final PodVlanMapVO podVlanMapVO = new PodVlanMapVO(podId, vlan.getId()); @@ -3223,6 +3240,13 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati isAccountSpecific = true; } + boolean isDomainSpecific = false; + List domainVln = _domainVlanMapDao.listDomainVlanMapsByVlan(vlanRange.getId()); + // Check for domain wide pool. It will have an entry for domain_vlan_map. + if (domainVln != null && !domainVln.isEmpty()) { + isDomainSpecific = true; + } + // Check if the VLAN has any allocated public IPs final List ips = _publicIpAddressDao.listByVlanId(vlanDbId); if (isAccountSpecific) { @@ -3315,15 +3339,24 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Unable to find project by id " + projectId); } vlanOwner = _accountMgr.getAccount(project.getProjectAccountId()); + if (vlanOwner == null) { + throw new InvalidParameterValueException("Please specify a valid projectId"); + } } + Domain domain = null; if (accountName != null && domainId != null) { vlanOwner = _accountDao.findActiveAccount(accountName, domainId); - } - if (vlanOwner == null) { - throw new InvalidParameterValueException("Unable to find account by name " + accountName); - } else if (vlanOwner.getId() == Account.ACCOUNT_ID_SYSTEM) { - throw new InvalidParameterValueException("Please specify a valid account. Cannot dedicate IP range to system account"); + if (vlanOwner == null) { + throw new InvalidParameterValueException("Unable to find account by name " + accountName); + } else if (vlanOwner.getId() == Account.ACCOUNT_ID_SYSTEM) { + throw new InvalidParameterValueException("Please specify a valid account. Cannot dedicate IP range to system account"); + } + } else if (domainId != null) { + domain = _domainDao.findById(domainId); + if (domain == null) { + throw new InvalidParameterValueException("Please specify a valid domain id"); + } } // Check if range is valid @@ -3338,6 +3371,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Specified Public IP range has already been dedicated"); } + List domainmaps = _domainVlanMapDao.listDomainVlanMapsByVlan(vlanDbId); + if (domainmaps != null && !domainmaps.isEmpty()) { + throw new InvalidParameterValueException("Specified Public IP range has already been dedicated to a domain"); + } + // Verify that zone exists and is advanced final Long zoneId = vlan.getDataCenterId(); final DataCenterVO zone = _zoneDao.findById(zoneId); @@ -3349,8 +3387,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } // Check Public IP resource limits - final int accountPublicIpRange = _publicIpAddressDao.countIPs(zoneId, vlanDbId, false); - _resourceLimitMgr.checkResourceLimit(vlanOwner, ResourceType.public_ip, accountPublicIpRange); + if (vlanOwner != null) { + final int accountPublicIpRange = _publicIpAddressDao.countIPs(zoneId, vlanDbId, false); + _resourceLimitMgr.checkResourceLimit(vlanOwner, ResourceType.public_ip, accountPublicIpRange); + } // Check if any of the Public IP addresses is allocated to another // account @@ -3362,21 +3402,33 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (!accountAllocatedTo.getAccountName().equalsIgnoreCase(accountName)) { throw new InvalidParameterValueException(ip.getAddress() + " Public IP address in range is allocated to another account "); } + if (vlanOwner == null && domain != null && domain.getId() != accountAllocatedTo.getDomainId()){ + throw new InvalidParameterValueException(ip.getAddress() + + " Public IP address in range is allocated to another domain/account "); + } } } - // Create an AccountVlanMapVO entry - final AccountVlanMapVO accountVlanMapVO = new AccountVlanMapVO(vlanOwner.getId(), vlan.getId()); - _accountVlanMapDao.persist(accountVlanMapVO); + if (vlanOwner != null) { + // Create an AccountVlanMapVO entry + final AccountVlanMapVO accountVlanMapVO = new AccountVlanMapVO(vlanOwner.getId(), vlan.getId()); + _accountVlanMapDao.persist(accountVlanMapVO); - // generate usage event for dedication of every ip address in the range - for (final IPAddressVO ip : ips) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, vlanOwner.getId(), ip.getDataCenterId(), ip.getId(), ip.getAddress().toString(), ip.isSourceNat(), - vlan.getVlanType().toString(), ip.getSystem(), ip.getClass().getName(), ip.getUuid()); + // generate usage event for dedication of every ip address in the range + for (final IPAddressVO ip : ips) { + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, vlanOwner.getId(), ip.getDataCenterId(), ip.getId(), ip.getAddress().toString(), ip.isSourceNat(), + vlan.getVlanType().toString(), ip.getSystem(), ip.getClass().getName(), ip.getUuid()); + } + } else if (domain != null) { + // Create an DomainVlanMapVO entry + DomainVlanMapVO domainVlanMapVO = new DomainVlanMapVO(domain.getId(), vlan.getId()); + _domainVlanMapDao.persist(domainVlanMapVO); } // increment resource count for dedicated public ip's - _resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, new Long(ips.size())); + if (vlanOwner != null) { + _resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, new Long(ips.size())); + } return vlan; } @@ -3398,12 +3450,25 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati public boolean releasePublicIpRange(final long vlanDbId, final long userId, final Account caller) { VlanVO vlan = _vlanDao.findById(vlanDbId); + // Verify range is dedicated + boolean isAccountSpecific = false; final List acctVln = _accountVlanMapDao.listAccountVlanMapsByVlan(vlanDbId); // Verify range is dedicated - if (acctVln == null || acctVln.isEmpty()) { - throw new InvalidParameterValueException("Can't release Public IP range " + vlanDbId + " as it not dedicated to any account"); + if (acctVln != null && !acctVln.isEmpty()) { + isAccountSpecific = true; } + boolean isDomainSpecific = false; + final List domainVln = _domainVlanMapDao.listDomainVlanMapsByVlan(vlanDbId); + // Check for domain wide pool. It will have an entry for domain_vlan_map. + if (domainVln != null && !domainVln.isEmpty()) { + isDomainSpecific = true; + } + + if (!isAccountSpecific && !isDomainSpecific) { + throw new InvalidParameterValueException("Can't release Public IP range " + vlanDbId + + " as it not dedicated to any domain and any account"); + } // Check if range has any allocated public IPs final long allocIpCount = _publicIpAddressDao.countIPs(vlan.getDataCenterId(), vlanDbId, true); final List ips = _publicIpAddressDao.listByVlanId(vlanDbId); @@ -3438,7 +3503,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } // A Public IP range can only be dedicated to one account at a time - if (_accountVlanMapDao.remove(acctVln.get(0).getId())) { + if (isAccountSpecific && _accountVlanMapDao.remove(acctVln.get(0).getId())) { // generate usage events to remove dedication for every ip in the range that has been disassociated for (final IPAddressVO ip : ips) { if (!ipsInUse.contains(ip)) { @@ -3449,6 +3514,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // decrement resource count for dedicated public ip's _resourceLimitMgr.decrementResourceCount(acctVln.get(0).getAccountId(), ResourceType.public_ip, new Long(ips.size())); return true; + } else if (isDomainSpecific && _domainVlanMapDao.remove(domainVln.get(0).getId())) { + s_logger.debug("Remove the vlan from domain_vlan_map successfully."); + return true; } else { return false; } @@ -4831,13 +4899,24 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } - final Long networkId = vlan.getNetworkId(); - if (networkId != null) { - final Network network = _networkModel.getNetwork(networkId); - if (network != null) { - return _accountMgr.getAccount(network.getAccountId()); + return null; + } + + @Override + public Domain getVlanDomain(long vlanId) { + Vlan vlan = _vlanDao.findById(vlanId); + Long domainId = null; + + // if vlan is Virtual Domain specific, get vlan information from the + // accountVlanMap; otherwise get account information + // from the network + if (vlan.getVlanType() == VlanType.VirtualNetwork) { + List maps = _domainVlanMapDao.listDomainVlanMapsByVlan(vlanId); + if (maps != null && !maps.isEmpty()) { + return _domainDao.findById(maps.get(0).getDomainId()); } } + return null; } diff --git a/server/src/com/cloud/network/IpAddressManagerImpl.java b/server/src/com/cloud/network/IpAddressManagerImpl.java index 105ad737962..a2f2cdaa096 100644 --- a/server/src/com/cloud/network/IpAddressManagerImpl.java +++ b/server/src/com/cloud/network/IpAddressManagerImpl.java @@ -50,6 +50,7 @@ import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.AccountVlanMapVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.DomainVlanMapVO; import com.cloud.dc.Pod; import com.cloud.dc.PodVlanMapVO; import com.cloud.dc.Vlan; @@ -58,6 +59,7 @@ import com.cloud.dc.VlanVO; import com.cloud.dc.dao.AccountVlanMapDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterVnetDao; +import com.cloud.dc.dao.DomainVlanMapDao; import com.cloud.dc.dao.PodVlanMapDao; import com.cloud.dc.dao.VlanDao; import com.cloud.deploy.DeployDestination; @@ -199,6 +201,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage @Inject AccountVlanMapDao _accountVlanMapDao; @Inject + DomainVlanMapDao _domainVlanMapDao; + @Inject NetworkOfferingDao _networkOfferingDao = null; @Inject NetworkDao _networksDao = null; @@ -685,6 +689,11 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage if (vlanDbIds == null || vlanDbIds.contains(map.getVlanDbId())) dedicatedVlanDbIds.add(map.getVlanDbId()); } + List domainMaps = _domainVlanMapDao.listDomainVlanMapsByDomain(owner.getDomainId()); + for (DomainVlanMapVO map : domainMaps) { + if (vlanDbIds == null || vlanDbIds.contains(map.getVlanDbId())) + dedicatedVlanDbIds.add(map.getVlanDbId()); + } List nonDedicatedVlans = _vlanDao.listZoneWideNonDedicatedVlans(dcId); for (VlanVO nonDedicatedVlan : nonDedicatedVlans) { if (vlanDbIds == null || vlanDbIds.contains(nonDedicatedVlan.getId())) diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index e5c13859d66..349499994b7 100644 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -1393,7 +1393,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (_accountMgr.isRootAdmin(caller.getId()) && createVlan && network != null) { // Create vlan ip range _configMgr.createVlanAndPublicIpRange(pNtwk.getDataCenterId(), network.getId(), physicalNetworkId, false, null, startIP, endIP, gateway, netmask, vlanId, - null, startIPv6, endIPv6, ip6Gateway, ip6Cidr); + null, null, startIPv6, endIPv6, ip6Gateway, ip6Cidr); } return network; } diff --git a/server/test/com/cloud/configuration/ConfigurationManagerTest.java b/server/test/com/cloud/configuration/ConfigurationManagerTest.java index d30dacf9a69..496b5a01778 100644 --- a/server/test/com/cloud/configuration/ConfigurationManagerTest.java +++ b/server/test/com/cloud/configuration/ConfigurationManagerTest.java @@ -59,6 +59,7 @@ import com.cloud.dc.dao.AccountVlanMapDao; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterIpAddressDao; +import com.cloud.dc.dao.DomainVlanMapDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.dc.dao.VlanDao; import com.cloud.exception.InvalidParameterValueException; @@ -115,6 +116,8 @@ public class ConfigurationManagerTest { @Mock AccountVlanMapDao _accountVlanMapDao; @Mock + DomainVlanMapDao _domainVlanMapDao; + @Mock IPAddressDao _publicIpAddressDao; @Mock DataCenterDao _zoneDao; @@ -156,6 +159,7 @@ public class ConfigurationManagerTest { configurationMgr._accountDao = _accountDao; configurationMgr._vlanDao = _vlanDao; configurationMgr._accountVlanMapDao = _accountVlanMapDao; + configurationMgr._domainVlanMapDao = _domainVlanMapDao; configurationMgr._publicIpAddressDao = _publicIpAddressDao; configurationMgr._zoneDao = _zoneDao; configurationMgr._firewallDao = _firewallDao; @@ -470,10 +474,11 @@ public class ConfigurationManagerTest { when(configurationMgr._vlanDao.findById(anyLong())).thenReturn(vlan); when(configurationMgr._accountVlanMapDao.listAccountVlanMapsByVlan(anyLong())).thenReturn(null); + when(configurationMgr._domainVlanMapDao.listDomainVlanMapsByVlan(anyLong())).thenReturn(null); try { configurationMgr.releasePublicIpRange(releasePublicIpRangesCmd); } catch (Exception e) { - Assert.assertTrue(e.getMessage().contains("as it not dedicated to any account")); + Assert.assertTrue(e.getMessage().contains("as it not dedicated to any domain and any account")); } finally { txn.close("runReleaseNonDedicatedPublicIpRange"); } @@ -561,15 +566,6 @@ public class ConfigurationManagerTest { } } - @Test - public void getVlanAccount() { - Mockito.when(_vlanDao.findById(42l)).thenReturn(vlan); - Mockito.when(_networkModel.getNetwork(1l)).thenReturn(network); - Mockito.when(network.getAccountId()).thenReturn(1l); - Mockito.when(_accountMgr.getAccount(1l)).thenReturn(account); - Assert.assertNotNull(configurationMgr.getVlanAccount(42l)); - } - @Test public void checkIfPodIsDeletableSuccessTest() { HostPodVO hostPodVO = Mockito.mock(HostPodVO.class); @@ -795,4 +791,4 @@ public class ConfigurationManagerTest { configurationMgr.checkIfZoneIsDeletable(new Random().nextLong()); } -} \ No newline at end of file +} diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java index ee315bdc640..28a6d96f733 100644 --- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -60,6 +60,7 @@ import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.Pod; import com.cloud.dc.Vlan; +import com.cloud.domain.Domain; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; @@ -439,7 +440,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu */ @Override public Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, Long podId, String startIP, String endIP, - String vlanGateway, String vlanNetmask, String vlanId, Account vlanOwner, String startIPv6, String endIPv6, String vlanGatewayv6, String vlanCidrv6) + String vlanGateway, String vlanNetmask, String vlanId, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanGatewayv6, String vlanCidrv6) throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException { // TODO Auto-generated method stub return null; @@ -522,4 +523,10 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu return false; } -} \ No newline at end of file + @Override + public Domain getVlanDomain(long vlanId) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/server/test/com/cloud/vpc/VpcTestConfiguration.java b/server/test/com/cloud/vpc/VpcTestConfiguration.java index 8777698db86..dcc44d6dc83 100644 --- a/server/test/com/cloud/vpc/VpcTestConfiguration.java +++ b/server/test/com/cloud/vpc/VpcTestConfiguration.java @@ -44,6 +44,7 @@ import com.cloud.dc.dao.DataCenterDetailsDaoImpl; import com.cloud.dc.dao.DataCenterIpAddressDaoImpl; import com.cloud.dc.dao.DataCenterLinkLocalIpAddressDaoImpl; import com.cloud.dc.dao.DataCenterVnetDaoImpl; +import com.cloud.dc.dao.DomainVlanMapDaoImpl; import com.cloud.dc.dao.HostPodDaoImpl; import com.cloud.dc.dao.PodVlanDaoImpl; import com.cloud.dc.dao.PodVlanMapDaoImpl; @@ -110,7 +111,7 @@ import com.cloud.vpc.dao.MockVpcOfferingServiceMapDaoImpl; @ComponentScan(basePackageClasses = {VpcManagerImpl.class, NetworkElement.class, VpcOfferingDao.class, ConfigurationDaoImpl.class, IPAddressDaoImpl.class, DomainRouterDaoImpl.class, VpcGatewayDaoImpl.class, PrivateIpDaoImpl.class, StaticRouteDaoImpl.class, PhysicalNetworkDaoImpl.class, ResourceTagsDaoImpl.class, FirewallRulesDaoImpl.class, VlanDaoImpl.class, AccountDaoImpl.class, ResourceCountDaoImpl.class, - Site2SiteVpnGatewayDaoImpl.class, PodVlanMapDaoImpl.class, AccountVlanMapDaoImpl.class, HostDaoImpl.class, HostDetailsDaoImpl.class, + Site2SiteVpnGatewayDaoImpl.class, PodVlanMapDaoImpl.class, AccountVlanMapDaoImpl.class, DomainVlanMapDaoImpl.class, HostDaoImpl.class, HostDetailsDaoImpl.class, HostTagsDaoImpl.class, HostTransferMapDaoImpl.class, ClusterDaoImpl.class, HostPodDaoImpl.class, RouterNetworkDaoImpl.class, UserStatisticsDaoImpl.class, PhysicalNetworkTrafficTypeDaoImpl.class, FirewallRulesCidrsDaoImpl.class, ResourceLimitManagerImpl.class, ResourceLimitDaoImpl.class, ResourceCountDaoImpl.class, DomainDaoImpl.class, UserVmDaoImpl.class, UserVmDetailsDaoImpl.class, NicDaoImpl.class, diff --git a/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java b/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java index 47601440432..343589c391e 100644 --- a/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java +++ b/server/test/org/apache/cloudstack/networkoffering/ChildTestConfiguration.java @@ -58,6 +58,7 @@ import com.cloud.dc.dao.DataCenterIpAddressDaoImpl; import com.cloud.dc.dao.DataCenterLinkLocalIpAddressDao; import com.cloud.dc.dao.DataCenterVnetDaoImpl; import com.cloud.dc.dao.DedicatedResourceDao; +import com.cloud.dc.dao.DomainVlanMapDaoImpl; import com.cloud.dc.dao.HostPodDaoImpl; import com.cloud.dc.dao.PodVlanDaoImpl; import com.cloud.dc.dao.PodVlanMapDaoImpl; @@ -121,7 +122,7 @@ import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDaoImpl; @Configuration -@ComponentScan(basePackageClasses = {AccountVlanMapDaoImpl.class, VolumeDaoImpl.class, HostPodDaoImpl.class, DomainDaoImpl.class, ServiceOfferingDaoImpl.class, +@ComponentScan(basePackageClasses = {AccountVlanMapDaoImpl.class, DomainVlanMapDaoImpl.class, VolumeDaoImpl.class, HostPodDaoImpl.class, DomainDaoImpl.class, ServiceOfferingDaoImpl.class, ServiceOfferingDetailsDaoImpl.class, VlanDaoImpl.class, IPAddressDaoImpl.class, ResourceTagsDaoImpl.class, AccountDaoImpl.class, InstanceGroupDaoImpl.class, UserAccountJoinDaoImpl.class, CapacityDaoImpl.class, SnapshotDaoImpl.class, HostDaoImpl.class, VMInstanceDaoImpl.class, HostTransferMapDaoImpl.class, PortForwardingRulesDaoImpl.class, PrivateIpDaoImpl.class, UsageEventDaoImpl.class, PodVlanMapDaoImpl.class, diff --git a/setup/db/db/schema-460to470.sql b/setup/db/db/schema-460to470.sql index b594ceb508d..a2f12d99f75 100644 --- a/setup/db/db/schema-460to470.sql +++ b/setup/db/db/schema-460to470.sql @@ -18,3 +18,15 @@ --; -- Schema upgrade from 4.6.0 to 4.7.0; --; + +CREATE TABLE IF NOT EXISTS `cloud`.`domain_vlan_map` ( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT, + `domain_id` bigint unsigned NOT NULL COMMENT 'domain id. foreign key to domain table', + `vlan_db_id` bigint unsigned NOT NULL COMMENT 'database id of vlan. foreign key to vlan table', + PRIMARY KEY (`id`), + CONSTRAINT `fk_domain_vlan_map__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain` (`id`) ON DELETE CASCADE, + INDEX `i_account_vlan_map__domain_id`(`domain_id`), + CONSTRAINT `fk_domain_vlan_map__vlan_id` FOREIGN KEY (`vlan_db_id`) REFERENCES `vlan` (`id`) ON DELETE CASCADE, + INDEX `i_account_vlan_map__vlan_id`(`vlan_db_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + diff --git a/setup/db/db/schema-461to470.sql b/setup/db/db/schema-461to470.sql index d9e38238a63..15e9b38ed4e 100644 --- a/setup/db/db/schema-461to470.sql +++ b/setup/db/db/schema-461to470.sql @@ -18,3 +18,15 @@ --; -- Schema upgrade from 4.6.1 to 4.7.0; --; + +CREATE TABLE IF NOT EXISTS `cloud`.`domain_vlan_map` ( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT, + `domain_id` bigint unsigned NOT NULL COMMENT 'domain id. foreign key to domain table', + `vlan_db_id` bigint unsigned NOT NULL COMMENT 'database id of vlan. foreign key to vlan table', + PRIMARY KEY (`id`), + CONSTRAINT `fk_domain_vlan_map__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain` (`id`) ON DELETE CASCADE, + INDEX `i_account_vlan_map__domain_id`(`domain_id`), + CONSTRAINT `fk_domain_vlan_map__vlan_id` FOREIGN KEY (`vlan_db_id`) REFERENCES `vlan` (`id`) ON DELETE CASCADE, + INDEX `i_account_vlan_map__vlan_id`(`vlan_db_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + diff --git a/ui/scripts/system.js b/ui/scripts/system.js index eaf2e0daab8..33aafc50dec 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -83,9 +83,14 @@ success: function (json) { var domain = json.listdomainsresponse.domain[0]; - cloudStack.dialog.notice({ - message: '
  • ' + _l('label.account') + ': ' + data.account + '
  • ' + '
  • ' + _l('label.domain') + ': ' + domain.path + '
' - }); + if (data.account != null) + cloudStack.dialog.notice({ + message: '
  • ' + _l('label.account') + ': ' + data.account + '
  • ' + '
  • ' + _l('label.domain') + ': ' + domain.path + '
' + }); + else + cloudStack.dialog.notice({ + message: '
  • ' + _l('label.domain') + ': ' + domain.path + '
' + }); } }); } else { @@ -743,7 +748,8 @@ array1.push("&endip=" + args.data.endip); if (args.data.account) { - array1.push("&account=" + args.data.account.account); + if (args.data.account.account) + array1.push("&account=" + args.data.account.account); array1.push("&domainid=" + args.data.account.domainid); } @@ -774,7 +780,7 @@ }, actionPreFilter: function (args) { var actionsToShow =[ 'destroy']; - if (args.context.multiRule[0].domain == 'ROOT' && args.context.multiRule[0].account.account == 'system') + if (args.context.multiRule[0].domain == 'ROOT' && args.context.multiRule[0].account != null && args.context.multiRule[0].account.account == 'system') actionsToShow.push('addAccount'); else actionsToShow.push('releaseFromAccount'); return actionsToShow; @@ -865,8 +871,12 @@ id: args.context.multiRule[0].id, zoneid: args.context.multiRule[0].zoneid, domainid: args.data.domainid, - account: args.data.account }; + if (args.data.account) { + $.extend(data, { + account: args.data.account + }); + } $.ajax({ url: createURL('dedicatePublicIpRange'), data: data, @@ -898,7 +908,7 @@ data: $.map(items, function (item) { return $.extend(item, { account: { - _buttonLabel: item.account, + _buttonLabel: item.account ? '[' + item.domain + '] ' + item.account: item.domain, account: item.account, domainid: item.domainid }