diff --git a/api/src/com/cloud/configuration/ConfigurationService.java b/api/src/com/cloud/configuration/ConfigurationService.java index b13122208ee..5af44ed18f4 100644 --- a/api/src/com/cloud/configuration/ConfigurationService.java +++ b/api/src/com/cloud/configuration/ConfigurationService.java @@ -19,7 +19,9 @@ package com.cloud.configuration; import java.util.List; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; +import org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd; import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; +import org.apache.cloudstack.api.command.admin.network.DeleteManagementNetworkIpRangeCmd; import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.offering.CreateDiskOfferingCmd; @@ -160,6 +162,19 @@ public interface ConfigurationService { */ Pod createPod(long zoneId, String name, String startIp, String endIp, String gateway, String netmask, String allocationState); + /** + * Creates a mutual exclusive IP range in the pod with same gateway, netmask. + * @param cmd - The command specifying pod ID, start IP, end IP, gateway, netmask. + * @return The new range if successful, null otherwise. + */ + Pod createPodIpRange(CreateManagementNetworkIpRangeCmd cmd); + + /** + * Deletes a mutually exclusive IP range in the pod. + * @param cmd - The command specifying pod ID, start IP, end IP. + */ + void deletePodIpRange(DeleteManagementNetworkIpRangeCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException; + /** * Edits a pod in the database. Will not allow you to edit pods that are being used anywhere in the system. * diff --git a/api/src/com/cloud/dc/StorageNetworkIpRange.java b/api/src/com/cloud/dc/StorageNetworkIpRange.java index 2b50ca1594e..03d4f44de48 100644 --- a/api/src/com/cloud/dc/StorageNetworkIpRange.java +++ b/api/src/com/cloud/dc/StorageNetworkIpRange.java @@ -21,20 +21,19 @@ import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; public interface StorageNetworkIpRange extends InfrastructureEntity, InternalIdentity, Identity { - Integer getVlan(); - String getPodUuid(); + String getGateway(); + + String getNetmask(); String getStartIp(); String getEndIp(); - String getNetworkUuid(); - String getZoneUuid(); - String getNetmask(); + String getPodUuid(); - String getGateway(); + String getNetworkUuid(); } diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index ec3f3ac6d10..d5d11e87702 100644 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -296,6 +296,9 @@ public class EventTypes { public static final String EVENT_VLAN_IP_RANGE_DEDICATE = "VLAN.IP.RANGE.DEDICATE"; public static final String EVENT_VLAN_IP_RANGE_RELEASE = "VLAN.IP.RANGE.RELEASE"; + public static final String EVENT_MANAGEMENT_IP_RANGE_CREATE = "MANAGEMENT.IP.RANGE.CREATE"; + public static final String EVENT_MANAGEMENT_IP_RANGE_DELETE = "MANAGEMENT.IP.RANGE.DELETE"; + public static final String EVENT_STORAGE_IP_RANGE_CREATE = "STORAGE.IP.RANGE.CREATE"; public static final String EVENT_STORAGE_IP_RANGE_DELETE = "STORAGE.IP.RANGE.DELETE"; public static final String EVENT_STORAGE_IP_RANGE_UPDATE = "STORAGE.IP.RANGE.UPDATE"; @@ -761,6 +764,9 @@ public class EventTypes { entityEventDetails.put(EVENT_VLAN_IP_RANGE_DEDICATE, Vlan.class); entityEventDetails.put(EVENT_VLAN_IP_RANGE_RELEASE, Vlan.class); + entityEventDetails.put(EVENT_MANAGEMENT_IP_RANGE_CREATE, Pod.class); + entityEventDetails.put(EVENT_MANAGEMENT_IP_RANGE_DELETE, Pod.class); + entityEventDetails.put(EVENT_STORAGE_IP_RANGE_CREATE, StorageNetworkIpRange.class); entityEventDetails.put(EVENT_STORAGE_IP_RANGE_DELETE, StorageNetworkIpRange.class); entityEventDetails.put(EVENT_STORAGE_IP_RANGE_UPDATE, StorageNetworkIpRange.class); diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/CreateManagementNetworkIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/CreateManagementNetworkIpRangeCmd.java new file mode 100644 index 00000000000..e5bfc07c699 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/network/CreateManagementNetworkIpRangeCmd.java @@ -0,0 +1,145 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.network; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.PodResponse; + +import com.cloud.dc.Pod; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; + +@APICommand(name = CreateManagementNetworkIpRangeCmd.APINAME, + description = "Creates a Management network IP range.", + responseObject = PodResponse.class, + since = "4.11.0.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class CreateManagementNetworkIpRangeCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(CreateManagementNetworkIpRangeCmd.class); + + public static final String APINAME = "createManagementNetworkIpRange"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name = ApiConstants.POD_ID, + type = CommandType.UUID, + entityType = PodResponse.class, + required = true, + description = "UUID of POD, where the IP range belongs to.", + validations = {ApiArgValidator.PositiveNumber}) + private Long podId; + + @Parameter(name = ApiConstants.GATEWAY, + type = CommandType.STRING, + required = true, + description = "The gateway for the management network.", + validations = {ApiArgValidator.NotNullOrEmpty}) + private String gateway; + + @Parameter(name = ApiConstants.NETMASK, + type = CommandType.STRING, + required = true, + description = "The netmask for the management network.", + validations = {ApiArgValidator.NotNullOrEmpty}) + private String netmask; + + @Parameter(name = ApiConstants.START_IP, + type = CommandType.STRING, + required = true, + description = "The starting IP address.", + validations = {ApiArgValidator.NotNullOrEmpty}) + private String startIp; + + @Parameter(name = ApiConstants.END_IP, + type = CommandType.STRING, + description = "The ending IP address.") + private String endIp; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getPodId() { + return podId; + } + + public String getGateWay() { + return gateway; + } + + public String getNetmask() { + return netmask; + } + + public String getStartIp() { + return startIp; + } + + public String getEndIp() { + return endIp; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_MANAGEMENT_IP_RANGE_CREATE; + } + + @Override + public String getEventDescription() { + return "Creating management ip range from " + getStartIp() + " to " + getEndIp() + " and gateway=" + getGateWay() + ", netmask=" + getNetmask() + " of pod=" + getPodId(); + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, + ResourceAllocationException { + Pod result = _configService.createPodIpRange(this); + if (result != null) { + PodResponse response = _responseGenerator.createPodResponse(result, false); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create Pod IP Range."); + } + } + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseAsyncCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/DeleteManagementNetworkIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/DeleteManagementNetworkIpRangeCmd.java new file mode 100644 index 00000000000..acb9e7a4678 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/network/DeleteManagementNetworkIpRangeCmd.java @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.network; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; + +@APICommand(name = DeleteManagementNetworkIpRangeCmd.APINAME, + description = "Deletes a management network IP range. This action is only allowed when no IPs in this range are allocated.", + responseObject = SuccessResponse.class, + since = "4.11.0.0", + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class DeleteManagementNetworkIpRangeCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DeleteManagementNetworkIpRangeCmd.class); + + public static final String APINAME = "deleteManagementNetworkIpRange"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.POD_ID, + type = CommandType.UUID, + entityType = PodResponse.class, + required = true, + description = "UUID of POD, where the IP range belongs to.", + validations = ApiArgValidator.PositiveNumber) + private Long podId; + + @Parameter(name = ApiConstants.START_IP, + type = CommandType.STRING, + required = true, + description = "The starting IP address.", + validations = ApiArgValidator.NotNullOrEmpty) + private String startIp; + + @Parameter(name = ApiConstants.END_IP, + type = CommandType.STRING, + required = true, + description = "The ending IP address.", + validations = ApiArgValidator.NotNullOrEmpty) + private String endIp; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getPodId() { + return podId; + } + + public String getStartIp() { + return startIp; + } + + public String getEndIp() { + return endIp; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_MANAGEMENT_IP_RANGE_DELETE; + } + + @Override + public String getEventDescription() { + return "Deleting management ip range from " + getStartIp() + " to " + getEndIp() + " of Pod: " + getPodId(); + } + + @Override + public void execute() { + try { + _configService.deletePodIpRange(this); + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } catch (ResourceUnavailableException ex) { + s_logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } catch (ConcurrentOperationException ex) { + s_logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } catch (Exception e) { + s_logger.warn("Failed to delete management ip range from " + getStartIp() + " to " + getEndIp() + " of Pod: " + getPodId(), e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseAsyncCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + +} \ No newline at end of file diff --git a/api/src/org/apache/cloudstack/api/response/PodResponse.java b/api/src/org/apache/cloudstack/api/response/PodResponse.java index 7ff0bfc8bcb..c5c700ef5df 100644 --- a/api/src/org/apache/cloudstack/api/response/PodResponse.java +++ b/api/src/org/apache/cloudstack/api/response/PodResponse.java @@ -55,11 +55,11 @@ public class PodResponse extends BaseResponse { @SerializedName("startip") @Param(description = "the starting IP for the Pod") - private String startIp; + private List startIp; @SerializedName("endip") @Param(description = "the ending IP for the Pod") - private String endIp; + private List endIp; @SerializedName("allocationstate") @Param(description = "the allocation state of the Pod") @@ -117,19 +117,19 @@ public class PodResponse extends BaseResponse { this.netmask = netmask; } - public String getStartIp() { + public List getStartIp() { return startIp; } - public void setStartIp(String startIp) { + public void setStartIp(List startIp) { this.startIp = startIp; } - public String getEndIp() { + public List getEndIp() { return endIp; } - public void setEndIp(String endIp) { + public void setEndIp(List endIp) { this.endIp = endIp; } diff --git a/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDao.java b/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDao.java index 3b988dac21f..9929cc3e4b2 100644 --- a/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDao.java +++ b/engine/schema/src/com/cloud/dc/dao/DataCenterIpAddressDao.java @@ -23,17 +23,17 @@ import com.cloud.utils.db.GenericDao; public interface DataCenterIpAddressDao extends GenericDao { - public DataCenterIpAddressVO takeIpAddress(long dcId, long podId, long instanceId, String reservationId); + DataCenterIpAddressVO takeIpAddress(long dcId, long podId, long instanceId, String reservationId); - public DataCenterIpAddressVO takeDataCenterIpAddress(long dcId, String reservationId); + DataCenterIpAddressVO takeDataCenterIpAddress(long dcId, String reservationId); - public void addIpRange(long dcId, long podId, String start, String end); + void addIpRange(long dcId, long podId, String start, String end); - public void releaseIpAddress(String ipAddress, long dcId, Long instanceId); + void releaseIpAddress(String ipAddress, long dcId, Long instanceId); - public void releaseIpAddress(long nicId, String reservationId); + void releaseIpAddress(long nicId, String reservationId); - public void releaseIpAddress(long nicId); + void releaseIpAddress(long nicId); boolean mark(long dcId, long podId, String ip); @@ -45,8 +45,11 @@ public interface DataCenterIpAddressDao extends GenericDao 0; } + @Override + public boolean deleteIpAddressByPodDc(String ipAddress, long podId, long dcId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("ipAddress", ipAddress); + sc.setParameters("pod", podId); + sc.setParameters("dc", dcId); + + return remove(sc) > 0; + } + @Override public boolean mark(long dcId, long podId, String ip) { SearchCriteria sc = AllFieldsSearch.create(); @@ -248,6 +258,22 @@ public class DataCenterIpAddressDaoImpl extends GenericDaoBase sc = createSearchCriteria(); + + if(onlyCountAllocated) { + sc.addAnd("takenAt", SearchCriteria.Op.NNULL); + } + + sc.addAnd("ipAddress", SearchCriteria.Op.EQ, ipAddress); + sc.addAnd("podId", SearchCriteria.Op.EQ, podId); + sc.addAnd("dataCenterId", SearchCriteria.Op.EQ, dcId); + + List result = listBy(sc); + return result.size(); + } + public DataCenterIpAddressDaoImpl() { super(); diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 3325be1069c..b0898baa2da 100644 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -945,10 +945,18 @@ public class ApiResponseHelper implements ResponseGenerator { @Override public PodResponse createPodResponse(Pod pod, Boolean showCapacities) { String[] ipRange = new String[2]; + List startIp = new ArrayList(); + List endIp = new ArrayList(); + if (pod.getDescription() != null && pod.getDescription().length() > 0) { - ipRange = pod.getDescription().split("-"); - } else { - ipRange[0] = pod.getDescription(); + final String[] existingPodIpRanges = pod.getDescription().split(","); + + for(String podIpRange: existingPodIpRanges) { + final String[] existingPodIpRange = podIpRange.split("-"); + + startIp.add(((existingPodIpRange.length > 0) && (existingPodIpRange[0] != null)) ? existingPodIpRange[0] : ""); + endIp.add(((existingPodIpRange.length > 1) && (existingPodIpRange[1] != null)) ? existingPodIpRange[1] : ""); + } } PodResponse podResponse = new PodResponse(); @@ -960,8 +968,8 @@ public class ApiResponseHelper implements ResponseGenerator { podResponse.setZoneName(zone.getName()); } podResponse.setNetmask(NetUtils.getCidrNetmask(pod.getCidrSize())); - podResponse.setStartIp(ipRange[0]); - podResponse.setEndIp(((ipRange.length > 1) && (ipRange[1] != null)) ? ipRange[1] : ""); + podResponse.setStartIp(startIp); + podResponse.setEndIp(endIp); podResponse.setGateway(pod.getGateway()); podResponse.setAllocationState(pod.getAllocationState().toString()); if (showCapacities != null && showCapacities) { diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 1bf807af7ea..034111ef428 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -41,7 +41,9 @@ import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupService; import org.apache.cloudstack.affinity.dao.AffinityGroupDao; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; +import org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd; import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; +import org.apache.cloudstack.api.command.admin.network.DeleteManagementNetworkIpRangeCmd; import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.offering.CreateDiskOfferingCmd; @@ -224,6 +226,7 @@ import com.cloud.vm.dao.NicSecondaryIpDao; import com.cloud.vm.dao.VMInstanceDao; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; +import com.google.common.base.Strings; public class ConfigurationManagerImpl extends ManagerBase implements ConfigurationManager, ConfigurationService, Configurable { public static final Logger s_logger = Logger.getLogger(ConfigurationManagerImpl.class); @@ -997,7 +1000,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati checkIpRange(startIp, endIp, cidrAddress, cidrSize); // Check if the IP range overlaps with the public ip - checkOverlapPublicIpRange(zoneId, startIp, endIp); + if(!Strings.isNullOrEmpty(startIp)) { + checkOverlapPublicIpRange(zoneId, startIp, endIp); + } // Check if the gateway is a valid IP address if (!NetUtils.isValidIp(gateway)) { @@ -1090,9 +1095,241 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati return true; } + @Override + @DB + public Pod createPodIpRange(final CreateManagementNetworkIpRangeCmd cmd) { + + final Account account = CallContext.current().getCallingAccount(); + + if(!_accountMgr.isRootAdmin(account.getId())) { + throw new PermissionDeniedException("Cannot perform this operation, Calling account is not root admin: " + account.getId()); + } + + final long podId = cmd.getPodId(); + final String gateway = cmd.getGateWay(); + final String netmask = cmd.getNetmask(); + final String startIp = cmd.getStartIp(); + String endIp = cmd.getEndIp(); + + final HostPodVO pod = _podDao.findById(podId); + + if(pod == null) { + throw new InvalidParameterValueException("Unable to find pod by ID: " + podId); + } + + final long zoneId = pod.getDataCenterId(); + + if(!NetUtils.isValidIp(gateway)) { + throw new InvalidParameterValueException("The gateway IP address is invalid."); + } + + if(!NetUtils.isValidNetmask(netmask)) { + throw new InvalidParameterValueException("The netmask IP address is invalid."); + } + + if(endIp == null) { + endIp = startIp; + } + + final String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask); + + if(!NetUtils.isValidCIDR(cidr)) { + throw new InvalidParameterValueException("The CIDR is invalid " + cidr); + } + + final String cidrAddress = pod.getCidrAddress(); + final long cidrSize = pod.getCidrSize(); + + // Because each pod has only one Gateway and Netmask. + if (!gateway.equals(pod.getGateway())) { + throw new InvalidParameterValueException("Multiple gateways for the POD: " + pod.getId() + " are not allowed. The Gateway should be same as the existing Gateway " + pod.getGateway()); + } + + if (!netmask.equals(NetUtils.getCidrNetmask(cidrSize))) { + throw new InvalidParameterValueException("Multiple subnets for the POD: " + pod.getId() + " are not allowed. The Netmask should be same as the existing Netmask " + NetUtils.getCidrNetmask(cidrSize)); + } + + // Check if the IP range is valid. + checkIpRange(startIp, endIp, cidrAddress, cidrSize); + + // Check if the IP range overlaps with the public ip. + checkOverlapPublicIpRange(zoneId, startIp, endIp); + + // Check if the gateway is in the CIDR subnet + if (!NetUtils.getCidrSubNet(gateway, cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) { + throw new InvalidParameterValueException("The gateway is not in the CIDR subnet."); + } + + if (NetUtils.ipRangesOverlap(startIp, endIp, gateway, gateway)) { + throw new InvalidParameterValueException("The gateway shouldn't overlap start/end ip addresses"); + } + + final String[] existingPodIpRanges = pod.getDescription().split(","); + + for(String podIpRange: existingPodIpRanges) { + final String[] existingPodIpRange = podIpRange.split("-"); + + if (existingPodIpRange.length > 1) { + if (!NetUtils.isValidIp(existingPodIpRange[0]) || !NetUtils.isValidIp(existingPodIpRange[1])) { + continue; + } + // Check if the range overlaps with any existing range. + if (NetUtils.ipRangesOverlap(startIp, endIp, existingPodIpRange[0], existingPodIpRange[1])) { + throw new InvalidParameterValueException("The new range overlaps with existing range. Please add a mutually exclusive range."); + } + } + } + + try { + final String endIpFinal = endIp; + + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(final TransactionStatus status) { + String ipRange = pod.getDescription(); + + if(ipRange != null && !ipRange.isEmpty()) + ipRange += ("," + startIp + "-" + endIpFinal); + else + ipRange = (startIp + "-" + endIpFinal); + + pod.setDescription(ipRange); + + HostPodVO lock = null; + try { + lock = _podDao.acquireInLockTable(podId); + + if (lock == null) { + String msg = "Unable to acquire lock on table to update the ip range of POD: " + pod.getName() + ", Creation failed."; + s_logger.warn(msg); + throw new CloudRuntimeException(msg); + } + + _podDao.update(podId, pod); + } finally { + if (lock != null) { + _podDao.releaseFromLockTable(podId); + } + } + + _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, endIpFinal); + } + }); + } catch (final Exception e) { + s_logger.error("Unable to create Pod IP range due to " + e.getMessage(), e); + throw new CloudRuntimeException("Failed to create Pod IP range. Please contact Cloud Support."); + } + + return pod; + } + + @Override + @DB + public void deletePodIpRange(final DeleteManagementNetworkIpRangeCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException { + final long podId = cmd.getPodId(); + final String startIp = cmd.getStartIp(); + final String endIp = cmd.getEndIp(); + + final HostPodVO pod = _podDao.findById(podId); + + if(pod == null) { + throw new InvalidParameterValueException("Unable to find pod by id " + podId); + } + + if (startIp == null || !NetUtils.isValidIp(startIp)) { + throw new InvalidParameterValueException("The start address of the IP range is not a valid IP address."); + } + + if (endIp == null || !NetUtils.isValidIp(endIp)) { + throw new InvalidParameterValueException("The end address of the IP range is not a valid IP address."); + } + + if (NetUtils.ip2Long(startIp) > NetUtils.ip2Long(endIp)) { + throw new InvalidParameterValueException("The start IP address must have a lower value than the end IP address."); + } + + for(long ipAddr = NetUtils.ip2Long(startIp); ipAddr <= NetUtils.ip2Long(endIp); ipAddr++) { + if(_privateIpAddressDao.countIpAddressUsage(NetUtils.long2Ip(ipAddr), podId, pod.getDataCenterId(), true) > 0) { + throw new CloudRuntimeException("Some IPs of the range has been allocated, so it cannot be deleted."); + } + } + + final String[] existingPodIpRanges = pod.getDescription().split(","); + + if(existingPodIpRanges.length == 0) { + throw new InvalidParameterValueException("The IP range cannot be found. As the existing IP range is empty."); + } + + final String[] newPodIpRanges = new String[existingPodIpRanges.length-1]; + int index = existingPodIpRanges.length-2; + boolean foundRange = false; + + for(String podIpRange: existingPodIpRanges) { + final String[] existingPodIpRange = podIpRange.split("-"); + + if(existingPodIpRange.length > 1) { + if (startIp.equals(existingPodIpRange[0]) && endIp.equals(existingPodIpRange[1])) { + foundRange = true; + } else if (index >= 0) { + newPodIpRanges[index--] = (existingPodIpRange[0] + "-" + existingPodIpRange[1]); + } + } + } + + if(!foundRange) { + throw new InvalidParameterValueException("The input IP range: " + startIp + "-" + endIp + " of pod: " + podId + "is not present. Please input an existing range."); + } + + final StringBuilder newPodIpRange = new StringBuilder(); + boolean first = true; + for (String podIpRange : newPodIpRanges) { + if (first) + first = false; + else + newPodIpRange.append(","); + + newPodIpRange.append(podIpRange); + } + + try { + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(final TransactionStatus status) { + pod.setDescription(newPodIpRange.toString()); + + HostPodVO lock = null; + try { + lock = _podDao.acquireInLockTable(podId); + + if (lock == null) { + String msg = "Unable to acquire lock on table to update the ip range of POD: " + pod.getName() + ", Deletion failed."; + s_logger.warn(msg); + throw new CloudRuntimeException(msg); + } + + _podDao.update(podId, pod); + } finally { + if (lock != null) { + _podDao.releaseFromLockTable(podId); + } + } + + for(long ipAddr = NetUtils.ip2Long(startIp); ipAddr <= NetUtils.ip2Long(endIp); ipAddr++) { + if (!_privateIpAddressDao.deleteIpAddressByPodDc(NetUtils.long2Ip(ipAddr), podId, pod.getDataCenterId())) { + throw new CloudRuntimeException("Failed to cleanup private ip address: " + NetUtils.long2Ip(ipAddr) + " of Pod: " + podId + " DC: " + pod.getDataCenterId()); + } + } + } + }); + } catch (final Exception e) { + s_logger.error("Unable to delete Pod " + podId + "IP range due to " + e.getMessage(), e); + throw new CloudRuntimeException("Failed to delete Pod " + podId + "IP range. Please contact Cloud Support."); + } + } + @Override public Pod editPod(final UpdatePodCmd cmd) { - return editPod(cmd.getId(), cmd.getPodName(), cmd.getStartIp(), cmd.getEndIp(), cmd.getGateway(), cmd.getNetmask(), cmd.getAllocationState()); + return editPod(cmd.getId(), cmd.getPodName(), null, null, cmd.getGateway(), cmd.getNetmask(), cmd.getAllocationState()); } @Override @@ -1106,16 +1343,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Unable to find pod by id " + id); } - final String[] existingPodIpRange = pod.getDescription().split("-"); - String[] leftRangeToAdd = null; - String[] rightRangeToAdd = null; - boolean allowToDownsize = false; - // If the gateway, CIDR, private IP range is being changed, check if the // pod has allocated private IP addresses if (podHasAllocatedPrivateIPs(id)) { - if (netmask != null) { + if (!Strings.isNullOrEmpty(netmask)) { final long newCidr = NetUtils.getCidrSize(netmask); final long oldCidr = pod.getCidrSize(); @@ -1123,31 +1355,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new CloudRuntimeException("The specified pod has allocated private IP addresses, so its IP address range can be extended only"); } } - - if (startIp != null && !startIp.equals(existingPodIpRange[0])) { - if (NetUtils.ipRangesOverlap(startIp, null, existingPodIpRange[0], existingPodIpRange[1])) { - throw new CloudRuntimeException("The specified pod has allocated private IP addresses, so its IP address range can be extended only"); - } else { - leftRangeToAdd = new String[2]; - final long endIpForUpdate = NetUtils.ip2Long(existingPodIpRange[0]) - 1; - leftRangeToAdd[0] = startIp; - leftRangeToAdd[1] = NetUtils.long2Ip(endIpForUpdate); - } - } - - if (endIp != null && !endIp.equals(existingPodIpRange[1])) { - if (NetUtils.ipRangesOverlap(endIp, endIp, existingPodIpRange[0], existingPodIpRange[1])) { - throw new CloudRuntimeException("The specified pod has allocated private IP addresses, so its IP address range can be extended only"); - } else { - rightRangeToAdd = new String[2]; - final long startIpForUpdate = NetUtils.ip2Long(existingPodIpRange[1]) + 1; - rightRangeToAdd[0] = NetUtils.long2Ip(startIpForUpdate); - rightRangeToAdd[1] = endIp; - } - } - - } else { - allowToDownsize = true; } if (gateway == null) { @@ -1163,18 +1370,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati name = oldPodName; } - if (gateway == null) { - gateway = pod.getGateway(); - } - - if (startIp == null) { - startIp = existingPodIpRange[0]; - } - - if (endIp == null) { - endIp = existingPodIpRange[1]; - } - if (allocationStateStr == null) { allocationStateStr = pod.getAllocationState().toString(); } @@ -1182,17 +1377,39 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // Verify pod's attributes final String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask); final boolean checkForDuplicates = !oldPodName.equals(name); - checkPodAttributes(id, name, pod.getDataCenterId(), gateway, cidr, startIp, endIp, allocationStateStr, checkForDuplicates, false); + checkPodAttributes(id, name, pod.getDataCenterId(), gateway, cidr, startIp, endIp, allocationStateStr, checkForDuplicates, true); + + // Valid check is already done in checkPodAttributes method. + final String cidrAddress = getCidrAddress(cidr); + final long cidrSize = getCidrSize(cidr); + + // Check if start IP and end IP of all the ranges lie in the CIDR subnet. + final String[] existingPodIpRanges = pod.getDescription().split(","); + + for(String podIpRange: existingPodIpRanges) { + final String[] existingPodIpRange = podIpRange.split("-"); + + if (existingPodIpRange.length > 1) { + if (!NetUtils.isValidIp(existingPodIpRange[0]) || !NetUtils.isValidIp(existingPodIpRange[1])) { + continue; + } + + if (!NetUtils.getCidrSubNet(existingPodIpRange[0], cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) { + throw new InvalidParameterValueException("The start address of the some IP range is not in the CIDR subnet."); + } + + if (!NetUtils.getCidrSubNet(existingPodIpRange[1], cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) { + throw new InvalidParameterValueException("The end address of the some IP range is not in the CIDR subnet."); + } + + if (NetUtils.ipRangesOverlap(existingPodIpRange[0], existingPodIpRange[1], gateway, gateway)) { + throw new InvalidParameterValueException("The gateway shouldn't overlap some start/end ip addresses"); + } + } + } try { - - final String[] existingPodIpRangeFinal = existingPodIpRange; - final String[] leftRangeToAddFinal = leftRangeToAdd; - final String[] rightRangeToAddFinal = rightRangeToAdd; - final boolean allowToDownsizeFinal = allowToDownsize; final String allocationStateStrFinal = allocationStateStr; - final String startIpFinal = startIp; - final String endIpFinal = endIp; final String nameFinal = name; final String gatewayFinal = gateway; Transaction.execute(new TransactionCallbackNoReturn() { @@ -1200,42 +1417,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati public void doInTransactionWithoutResult(final TransactionStatus status) { final long zoneId = pod.getDataCenterId(); - String startIp = startIpFinal; - String endIp = endIpFinal; - - if (!allowToDownsizeFinal) { - if (leftRangeToAddFinal != null) { - _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), leftRangeToAddFinal[0], leftRangeToAddFinal[1]); - } - - if (rightRangeToAddFinal != null) { - _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), rightRangeToAddFinal[0], rightRangeToAddFinal[1]); - } - - } else { - // delete the old range - _zoneDao.deletePrivateIpAddressByPod(pod.getId()); - - // add the new one - if (startIp == null) { - startIp = existingPodIpRangeFinal[0]; - } - - if (endIp == null) { - endIp = existingPodIpRangeFinal[1]; - } - - _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, endIp); - } - pod.setName(nameFinal); pod.setDataCenterId(zoneId); pod.setGateway(gatewayFinal); pod.setCidrAddress(getCidrAddress(cidr)); pod.setCidrSize(getCidrSize(cidr)); - final String ipRange = startIp + "-" + endIp; - pod.setDescription(ipRange); Grouping.AllocationState allocationState = null; if (allocationStateStrFinal != null && !allocationStateStrFinal.isEmpty()) { allocationState = Grouping.AllocationState.valueOf(allocationStateStrFinal); @@ -1297,7 +1484,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // endIp is an optional parameter; if not specified - default it to the // end ip of the pod's cidr - if (startIp != null) { + if (!Strings.isNullOrEmpty(startIp)) { if (endIp == null) { endIp = NetUtils.getIpRangeEndIpFromCidr(cidrAddress, cidrSize); } @@ -1308,7 +1495,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // Create the new pod in the database String ipRange; - if (startIp != null) { + if (!Strings.isNullOrEmpty(startIp)) { ipRange = startIp + "-" + endIp; } else { throw new InvalidParameterValueException("Start ip is required parameter"); @@ -1329,7 +1516,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati final HostPodVO pod = _podDao.persist(podFinal); - if (startIp != null) { + if (!Strings.isNullOrEmpty(startIp)) { _zoneDao.addPrivateIpAddress(zoneId, pod.getId(), startIp, endIpFinal); } @@ -1448,23 +1635,26 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } private void checkIpRange(final String startIp, final String endIp, final String cidrAddress, final long cidrSize) { - if (!NetUtils.isValidIp(startIp)) { + //Checking not null for start IP as well. Previously we assumed to be not null always. + //But the check is required for the change in updatePod API. + if (!Strings.isNullOrEmpty(startIp) && !NetUtils.isValidIp(startIp)) { throw new InvalidParameterValueException("The start address of the IP range is not a valid IP address."); } - if (endIp != null && !NetUtils.isValidIp(endIp)) { + if (!Strings.isNullOrEmpty(endIp) && !NetUtils.isValidIp(endIp)) { throw new InvalidParameterValueException("The end address of the IP range is not a valid IP address."); } - if (!NetUtils.getCidrSubNet(startIp, cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) { + //Not null check is required for the change in updatePod API. + if (!Strings.isNullOrEmpty(startIp) && !NetUtils.getCidrSubNet(startIp, cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) { throw new InvalidParameterValueException("The start address of the IP range is not in the CIDR subnet."); } - if (endIp != null && !NetUtils.getCidrSubNet(endIp, cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) { + if (!Strings.isNullOrEmpty(endIp) && !NetUtils.getCidrSubNet(endIp, cidrSize).equalsIgnoreCase(NetUtils.getCidrSubNet(cidrAddress, cidrSize))) { throw new InvalidParameterValueException("The end address of the IP range is not in the CIDR subnet."); } - if (endIp != null && NetUtils.ip2Long(startIp) > NetUtils.ip2Long(endIp)) { + if (!Strings.isNullOrEmpty(endIp) && NetUtils.ip2Long(startIp) > NetUtils.ip2Long(endIp)) { throw new InvalidParameterValueException("The start IP address must have a lower value than the end IP address."); } @@ -1487,15 +1677,20 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati final List podsInZone = _podDao.listByDataCenterId(zoneId); for (final HostPodVO hostPod : podsInZone) { - final String[] IpRange = hostPod.getDescription().split("-"); - if (IpRange[0] == null || IpRange[1] == null) { - continue; - } - if (!NetUtils.isValidIp(IpRange[0]) || !NetUtils.isValidIp(IpRange[1])) { - continue; - } - if (NetUtils.ipRangesOverlap(startIp, endIp, IpRange[0], IpRange[1])) { - throw new InvalidParameterValueException("The Start IP and endIP address range overlap with private IP :" + IpRange[0] + ":" + IpRange[1]); + final String[] existingPodIpRanges = hostPod.getDescription().split(","); + + for(String podIpRange: existingPodIpRanges) { + final String[] existingPodIpRange = podIpRange.split("-"); + + if (existingPodIpRange.length > 1) { + if (!NetUtils.isValidIp(existingPodIpRange[0]) || !NetUtils.isValidIp(existingPodIpRange[1])) { + continue; + } + + if (NetUtils.ipRangesOverlap(startIp, endIp, existingPodIpRange[0], existingPodIpRange[1])) { + throw new InvalidParameterValueException("The Start IP and EndIP address range overlap with private IP :" + existingPodIpRange[0] + ":" + existingPodIpRange[1]); + } + } } } } diff --git a/server/src/com/cloud/network/StorageNetworkManagerImpl.java b/server/src/com/cloud/network/StorageNetworkManagerImpl.java index d72de44885b..020f7b1b044 100644 --- a/server/src/com/cloud/network/StorageNetworkManagerImpl.java +++ b/server/src/com/cloud/network/StorageNetworkManagerImpl.java @@ -77,12 +77,21 @@ public class StorageNetworkManagerImpl extends ManagerBase implements StorageNet if (pod == null) { throw new CloudRuntimeException("Cannot find pod " + podId); } - String[] IpRange = pod.getDescription().split("-"); - if ((IpRange[0] == null || IpRange[1] == null) || (!NetUtils.isValidIp(IpRange[0]) || !NetUtils.isValidIp(IpRange[1]))) { - return; - } - if (NetUtils.ipRangesOverlap(startIp, endIp, IpRange[0], IpRange[1])) { - throw new InvalidParameterValueException("The Storage network Start IP and endIP address range overlap with private IP :" + IpRange[0] + ":" + IpRange[1]); + + final String[] existingPodIpRanges = pod.getDescription().split(","); + + for(String podIpRange: existingPodIpRanges) { + final String[] existingPodIpRange = podIpRange.split("-"); + + if (existingPodIpRange.length > 1) { + if (!NetUtils.isValidIp(existingPodIpRange[0]) || !NetUtils.isValidIp(existingPodIpRange[1])) { + continue; + } + + if (NetUtils.ipRangesOverlap(startIp, endIp, existingPodIpRange[0], existingPodIpRange[1])) { + throw new InvalidParameterValueException("The Storage network Start IP and endIP address range overlap with private IP :" + existingPodIpRange[0] + ":" + existingPodIpRange[1]); + } + } } } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 872a5020786..60628206e74 100644 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -105,11 +105,13 @@ import org.apache.cloudstack.api.command.admin.iso.RegisterIsoCmdByAdmin; import org.apache.cloudstack.api.command.admin.loadbalancer.ListLoadBalancerRuleInstancesCmdByAdmin; import org.apache.cloudstack.api.command.admin.network.AddNetworkDeviceCmd; import org.apache.cloudstack.api.command.admin.network.AddNetworkServiceProviderCmd; +import org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd; import org.apache.cloudstack.api.command.admin.network.CreateNetworkCmdByAdmin; import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.CreatePhysicalNetworkCmd; import org.apache.cloudstack.api.command.admin.network.CreateStorageNetworkIpRangeCmd; import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd; +import org.apache.cloudstack.api.command.admin.network.DeleteManagementNetworkIpRangeCmd; import org.apache.cloudstack.api.command.admin.network.DeleteNetworkDeviceCmd; import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.DeleteNetworkServiceProviderCmd; @@ -3010,6 +3012,11 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(UpdateLBHealthCheckPolicyCmd.class); cmdList.add(GetUploadParamsForTemplateCmd.class); cmdList.add(GetUploadParamsForVolumeCmd.class); + cmdList.add(AcquirePodIpCmdByAdmin.class); + cmdList.add(ReleasePodIpCmdByAdmin.class); + cmdList.add(CreateManagementNetworkIpRangeCmd.class); + cmdList.add(DeleteManagementNetworkIpRangeCmd.class); + // Out-of-band management APIs for admins cmdList.add(EnableOutOfBandManagementForHostCmd.class); cmdList.add(DisableOutOfBandManagementForHostCmd.class); @@ -3022,9 +3029,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(ChangeOutOfBandManagementPasswordCmd.class); cmdList.add(GetUserKeysCmd.class); - cmdList.add(AcquirePodIpCmdByAdmin.class); - cmdList.add(ReleasePodIpCmdByAdmin.class); - return cmdList; } diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java index e9e2b322511..a624986c446 100644 --- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -24,7 +24,9 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; +import org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd; import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; +import org.apache.cloudstack.api.command.admin.network.DeleteManagementNetworkIpRangeCmd; import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.offering.CreateDiskOfferingCmd; @@ -159,6 +161,24 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu return null; } + /* (non-Javadoc) + * @see com.cloud.configuration.ConfigurationService#createPodIpRange(org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd) + */ + @Override + public Pod createPodIpRange(CreateManagementNetworkIpRangeCmd cmd) { + // TODO Auto-generated method stub + return null; + } + + /* (non-Javadoc) + * @see com.cloud.configuration.ConfigurationService#deletePodIpRange(org.apache.cloudstack.api.command.admin.network.DeleteManagementNetworkIpRangeCmd) + */ + @Override + public void deletePodIpRange(DeleteManagementNetworkIpRangeCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException { + // TODO Auto-generated method stub + return; + } + /* (non-Javadoc) * @see com.cloud.configuration.ConfigurationService#editPod(org.apache.cloudstack.api.commands.UpdatePodCmd) */ diff --git a/ui/l10n/en.js b/ui/l10n/en.js index 19b5cd49a39..8c3cebbc48c 100644 --- a/ui/l10n/en.js +++ b/ui/l10n/en.js @@ -343,6 +343,7 @@ var dictionary = {"ICMP.code":"ICMP Code", "label.add.ldap.account":"Add LDAP account", "label.add.list.name":"ACL List Name", "label.add.load.balancer":"Add Load Balancer", +"label.add.management.ip.range":"Add Management IP Range", "label.add.more":"Add More", "label.add.netScaler.device":"Add Netscaler device", "label.add.network":"Add Network", @@ -1401,6 +1402,7 @@ var dictionary = {"ICMP.code":"ICMP Code", "label.remove.ingress.rule":"Remove ingress rule", "label.remove.ip.range":"Remove IP range", "label.remove.ldap":"Remove LDAP", +"label.remove.management.ip.range":"Remove Management IP Range", "label.remove.network.offering":"Remove network offering", "label.remove.pf":"Remove port forwarding rule", "label.remove.project.account":"Remove account from project", diff --git a/ui/scripts/system.js b/ui/scripts/system.js index fbcfb723a90..256ed1cfee5 100755 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -936,56 +936,154 @@ }); } }, + ipAddresses: { - //read-only listView (no actions) filled with pod info (not VlanIpRange info) title: 'label.ip.ranges', - listView: { - fields: { - name: { - label: 'label.pod' - }, - //pod name - gateway: { - label: 'label.gateway' - }, - //'Reserved system gateway' is too long and causes a visual format bug (2 lines overlay) - netmask: { - label: 'label.netmask' - }, - //'Reserved system netmask' is too long and causes a visual format bug (2 lines overlay) - startip: { - label: 'label.start.IP' - }, - //'Reserved system start IP' is too long and causes a visual format bug (2 lines overlay) - endip: { - label: 'label.end.IP' - } - //'Reserved system end IP' is too long and causes a visual format bug (2 lines overlay) - }, - dataProvider: function (args) { - var array1 =[]; - if (args.filterBy != null) { - if (args.filterBy.search != null && args.filterBy.search.by != null && args.filterBy.search.value != null) { - switch (args.filterBy.search.by) { - case "name": - if (args.filterBy.search.value.length > 0) - array1.push("&keyword=" + args.filterBy.search.value); - break; + custom: function (args) { + return $('
').multiEdit({ + context: args.context, + noSelect: true, + fields: { + 'podid': { + label: 'label.pod', + select: function (args) { + $.ajax({ + url: createURL("listPods&zoneid=" + selectedZoneObj.id), + dataType: "json", + success: function (json) { + var items =[]; + var pods = json.listpodsresponse.pod; + $(pods).each(function () { + items.push({ + name: this.id, + description: this.name + }); + }); + args.response.success({ + data: items + }); + } + }); } + }, + 'gateway': { + edit: true, + label: 'label.gateway' + }, + 'netmask': { + edit: true, + label: 'label.netmask' + }, + 'startip': { + edit: true, + label: 'label.start.IP' + }, + 'endip': { + edit: true, + label: 'label.end.IP', + validation: { + required: false + } + }, + 'add-rule': { + label: 'label.add', + addButton: true } - } - $.ajax({ - url: createURL("listPods&zoneid=" + selectedZoneObj.id + "&page=" + args.page + "&pagesize=" + pageSize + array1.join("")), - dataType: "json", - async: true, - success: function (json) { - var items = json.listpodsresponse.pod; - args.response.success({ - data: items + }, + add: { + label: 'label.add', + action: function (args) { + var array1 =[]; + + array1.push("&podid=" + args.data.podid); + array1.push("&gateway=" + args.data.gateway); + array1.push("&netmask=" + args.data.netmask); + array1.push("&startip=" + args.data.startip); + + if (args.data.endip != null && args.data.endip.length > 0) + array1.push("&endip=" + args.data.endip); + + $.ajax({ + url: createURL("createManagementNetworkIpRange" + array1.join("")), + dataType: "json", + success: function (json) { + args.response.success({ + _custom: { + jobId: json.createmanagementnetworkiprangeresponse.jobid + }, + notification: { + label: 'label.add.management.ip.range', + poll: pollAsyncJobResult + } + }); + }, + error: function (XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + args.response.error(errorMsg); + } }); } - }); - } + }, + actions: { + destroy: { + label: 'label.delete', + action: function (args) { + var array1 =[]; + array1.push("&podid=" + args.context.multiRule[0].podid); + array1.push("&startip=" + args.context.multiRule[0].startip); + array1.push("&endip=" + args.context.multiRule[0].endip); + + $.ajax({ + url: createURL('deleteManagementNetworkIpRange' + array1.join("")), + dataType: 'json', + async: true, + success: function (json) { + args.response.success({ + _custom: { + jobId: json.deletemanagementnetworkiprangeresponse.jobid + }, + notification: { + label: 'label.remove.management.ip.range', + poll: pollAsyncJobResult + } + }); + }, + error: function (XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + args.response.error(errorMsg); + } + }); + } + } + }, + dataProvider: function (args) { + $.ajax({ + url: createURL("listPods&zoneid=" + selectedZoneObj.id), + dataType: "json", + async: true, + success: function (json) { + var items =[]; + + var pods = json.listpodsresponse.pod; + $(pods).each(function () { + for (var i = 0; i < this.startip.length; i++) { + items.push({ + podid: this.id, + gateway: this.gateway, + netmask: this.netmask, + startip: this.startip[i], + endip: this.endip[i] + }); + } + }); + + args.response.success({ + data: items + }); + } + }); + } + }); } } } @@ -13465,11 +13563,10 @@ label: 'label.edit', action: function (args) { var array1 =[]; + array1.push("&name=" + todb(args.data.name)); array1.push("&netmask=" + todb(args.data.netmask)); - array1.push("&startIp=" + todb(args.data.startip)); - if (args.data.endip != null && args.data.endip.length > 0) - array1.push("&endIp=" + todb(args.data.endip)); + if (args.data.gateway != null && args.data.gateway.length > 0) array1.push("&gateway=" + todb(args.data.gateway)); @@ -13730,17 +13827,6 @@ required: true } }, - startip: { - label: 'label.start.IP', - isEditable: true, - validation: { - required: true - } - }, - endip: { - label: 'label.end.IP', - isEditable: true - }, gateway: { label: 'label.gateway', isEditable: true, @@ -13756,7 +13842,6 @@ label: 'label.allocation.state' } }, { - isdedicated: { label: 'label.dedicated' }, @@ -13766,13 +13851,10 @@ }], dataProvider: function (args) { - $.ajax({ url: createURL("listPods&id=" + args.context.pods[0].id), success: function (json) { var item = json.listpodsresponse.pod[0]; - - $.ajax({ url: createURL("listDedicatedPods&podid=" + args.context.pods[0].id), success: function (json) {