diff --git a/api/src/main/java/com/cloud/configuration/ConfigurationService.java b/api/src/main/java/com/cloud/configuration/ConfigurationService.java index 8b419027f70..6a51f7e1547 100644 --- a/api/src/main/java/com/cloud/configuration/ConfigurationService.java +++ b/api/src/main/java/com/cloud/configuration/ConfigurationService.java @@ -39,6 +39,7 @@ import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.UpdateVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; @@ -272,6 +273,12 @@ public interface ConfigurationService { Vlan createVlanAndPublicIpRange(CreateVlanIpRangeCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, ResourceAllocationException; + /** + * Updates the IP address Range for the VLAN on the database + * @return The Updated Vlan Object + */ + Vlan updateVlanAndPublicIpRange(UpdateVlanIpRangeCmd cmd) throws ConcurrentOperationException, + ResourceUnavailableException, ResourceAllocationException; /** * Marks the the account with the default zone-id. * diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java index 72e623f139a..808a8689606 100644 --- a/api/src/main/java/com/cloud/event/EventTypes.java +++ b/api/src/main/java/com/cloud/event/EventTypes.java @@ -324,6 +324,7 @@ public class EventTypes { public static final String EVENT_VLAN_IP_RANGE_DELETE = "VLAN.IP.RANGE.DELETE"; 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_VLAN_IP_RANGE_UPDATE = "VLAN.IP.RANGE.UPDATE"; 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"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmd.java new file mode 100644 index 00000000000..7a7775e4863 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmd.java @@ -0,0 +1,167 @@ +// 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.vlan; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.VlanIpRangeResponse; +import org.apache.log4j.Logger; + +import com.cloud.dc.Vlan; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.utils.net.NetUtils; + +@APICommand(name = UpdateVlanIpRangeCmd.APINAME, description = "Updates a VLAN IP range.", responseObject = + VlanIpRangeResponse.class, since = "4.16.0", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + authorized = {RoleType.Admin}) +public class UpdateVlanIpRangeCmd extends BaseCmd { + + public static final String APINAME = "updateVlanIpRange"; + public static final Logger s_logger = Logger.getLogger(UpdateVlanIpRangeCmd.class.getName()); + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VlanIpRangeResponse.class, required = true, + description = "the UUID of the VLAN IP range") + private Long id; + + @Parameter(name = ApiConstants.GATEWAY, type = CommandType.STRING, description = "the gateway of the VLAN IP range") + private String gateway; + + @Parameter(name = ApiConstants.NETMASK, type = CommandType.STRING, description = "the netmask of the VLAN IP range") + private String netmask; + + @Parameter(name = ApiConstants.START_IP, type = CommandType.STRING, description = "the beginning IP address in the VLAN IP range") + private String startIp; + + @Parameter(name = ApiConstants.END_IP, type = CommandType.STRING, + description = "the ending IP address in the VLAN IP range") + private String endIp; + + @Parameter(name = ApiConstants.START_IPV6, type = CommandType.STRING, description = "the beginning IPv6 address in the IPv6 network range") + private String startIpv6; + + @Parameter(name = ApiConstants.END_IPV6, type = CommandType.STRING, description = "the ending IPv6 address in the IPv6 network range") + private String endIpv6; + + @Parameter(name = ApiConstants.IP6_GATEWAY, type = CommandType.STRING, description = "the gateway of the IPv6 network") + private String ip6Gateway; + + @Parameter(name = ApiConstants.IP6_CIDR, type = CommandType.STRING, description = "the CIDR of IPv6 network, must be at least /64") + private String ip6Cidr; + + @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if IP range is set to system vms, false if not") + private Boolean forSystemVms; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + public Long getId() { + return id; + } + + public String getGateway() { + return gateway; + } + + public String getEndIp() { + return endIp; + } + + public String getNetmask() { + return netmask; + } + + public String getStartIp() { + return startIp; + } + + public String getStartIpv6() { + if (startIpv6 == null) { + return null; + } + return NetUtils.standardizeIp6Address(startIpv6); + } + + public String getEndIpv6() { + if (endIpv6 == null) { + return null; + } + return NetUtils.standardizeIp6Address(endIpv6); + } + + public String getIp6Gateway() { + if (ip6Gateway == null) { + return null; + } + return NetUtils.standardizeIp6Address(ip6Gateway); + } + + public String getIp6Cidr() { + if (ip6Cidr == null) { + return null; + } + return NetUtils.standardizeIp6Cidr(ip6Cidr); + } + + public Boolean isForSystemVms() { + return forSystemVms; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, ResourceAllocationException { + try { + Vlan result = _configService.updateVlanAndPublicIpRange(this); + if (result != null) { + VlanIpRangeResponse response = _responseGenerator.createVlanIpRangeResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to Update vlan ip range"); + } + } catch (ConcurrentOperationException ex) { + s_logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + } + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} \ No newline at end of file diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmdTest.java new file mode 100644 index 00000000000..280acbda689 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vlan/UpdateVlanIpRangeCmdTest.java @@ -0,0 +1,78 @@ +// 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.vlan; + +import junit.framework.TestCase; + +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.VlanIpRangeResponse; +import org.junit.Test; +import org.mockito.Mockito; + +import com.cloud.configuration.ConfigurationService; +import com.cloud.dc.Vlan; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; + +public class UpdateVlanIpRangeCmdTest extends TestCase { + + private UpdateVlanIpRangeCmd updateVlanIpRangeCmd; + private ResponseGenerator responseGenerator; + + @Test + public void testUpdateSuccess() throws Exception { + + ConfigurationService configService = Mockito.mock(ConfigurationService.class); + Vlan result = Mockito.mock(Vlan.class); + + responseGenerator = Mockito.mock(ResponseGenerator.class); + updateVlanIpRangeCmd = new UpdateVlanIpRangeCmd(); + + Mockito.when(configService.updateVlanAndPublicIpRange(updateVlanIpRangeCmd)).thenReturn(result); + updateVlanIpRangeCmd._configService = configService; + + VlanIpRangeResponse ipRes = Mockito.mock(VlanIpRangeResponse.class); + Mockito.when(responseGenerator.createVlanIpRangeResponse(result)).thenReturn(ipRes); + + updateVlanIpRangeCmd._responseGenerator = responseGenerator; + try { + updateVlanIpRangeCmd.execute(); + } catch (ServerApiException ex) { + assertEquals("Failed to Update vlan ip range", ex.getMessage()); + } + } + + @Test + public void testUpdateFailure() throws ResourceAllocationException, ResourceUnavailableException { + + ConfigurationService configService = Mockito.mock(ConfigurationService.class); + + responseGenerator = Mockito.mock(ResponseGenerator.class); + updateVlanIpRangeCmd = new UpdateVlanIpRangeCmd(); + updateVlanIpRangeCmd._configService = configService; + + Mockito.when(configService.updateVlanAndPublicIpRange(updateVlanIpRangeCmd)).thenReturn(null); + + try { + updateVlanIpRangeCmd.execute(); + } catch (ServerApiException ex) { + assertEquals("Failed to Update vlan ip range", ex.getMessage()); + } + + } +} \ No newline at end of file diff --git a/engine/schema/src/main/java/com/cloud/dc/VlanVO.java b/engine/schema/src/main/java/com/cloud/dc/VlanVO.java index ebbd4bde08b..20b07e983b8 100644 --- a/engine/schema/src/main/java/com/cloud/dc/VlanVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/VlanVO.java @@ -118,11 +118,19 @@ public class VlanVO implements Vlan { return vlanGateway; } + public void setVlanGateway(String vlanGateway) { + this.vlanGateway = vlanGateway; + } + @Override public String getVlanNetmask() { return vlanNetmask; } + public void setVlanNetmask(String vlanNetmask) { + this.vlanNetmask = vlanNetmask; + } + @Override public long getDataCenterId() { return dataCenterId; @@ -234,6 +242,6 @@ public class VlanVO implements Vlan { } public void setIpRange(String ipRange) { - this.ip6Range = ipRange; + this.ipRange = ipRange; } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java index 24d77db711a..8ee23008d71 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java @@ -19,6 +19,7 @@ package com.cloud.network.dao; import java.util.List; import com.cloud.dc.Vlan.VlanType; +import com.cloud.network.IpAddress.State; import com.cloud.utils.db.GenericDao; import com.cloud.utils.net.Ip; @@ -32,6 +33,8 @@ public interface IPAddressDao extends GenericDao { List listByVlanId(long vlanId); + List listByVlanIdAndState(long vlanId, State state); + List listByDcIdIpAddress(long dcId, String ipAddress); List listByDcId(long dcId); diff --git a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java index 43345b916fe..589f10a15f3 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java @@ -78,6 +78,7 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen AllFieldsSearch.and("dataCenterId", AllFieldsSearch.entity().getDataCenterId(), Op.EQ); AllFieldsSearch.and("ipAddress", AllFieldsSearch.entity().getAddress(), Op.EQ); AllFieldsSearch.and("vlan", AllFieldsSearch.entity().getVlanId(), Op.EQ); + AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), Op.EQ); AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAllocatedToAccountId(), Op.EQ); AllFieldsSearch.and("sourceNat", AllFieldsSearch.entity().isSourceNat(), Op.EQ); AllFieldsSearch.and("network", AllFieldsSearch.entity().getAssociatedWithNetworkId(), Op.EQ); @@ -471,4 +472,12 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen sc.setParameters("vlan", vlandbId); lockRows(sc, null, true); } + + @Override + public List listByVlanIdAndState(long vlanId, State state) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vlan", vlanId); + sc.setParameters("state", state); + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/UserIpv6AddressDao.java b/engine/schema/src/main/java/com/cloud/network/dao/UserIpv6AddressDao.java index 80c39d283a4..aee1cf2a01e 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/UserIpv6AddressDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/UserIpv6AddressDao.java @@ -18,6 +18,7 @@ package com.cloud.network.dao; import java.util.List; +import com.cloud.network.IpAddress; import com.cloud.network.UserIpv6AddressVO; import com.cloud.utils.db.GenericDao; @@ -26,6 +27,8 @@ public interface UserIpv6AddressDao extends GenericDao List listByVlanId(long vlanId); + List listByVlanIdAndState(long vlanId, IpAddress.State state); + List listByDcId(long dcId); List listByNetwork(long networkId); diff --git a/engine/schema/src/main/java/com/cloud/network/dao/UserIpv6AddressDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/UserIpv6AddressDaoImpl.java index 9a7737fe12c..08f0829f4ac 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/UserIpv6AddressDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/UserIpv6AddressDaoImpl.java @@ -19,6 +19,7 @@ package com.cloud.network.dao; import java.util.List; +import com.cloud.network.IpAddress; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -43,6 +44,7 @@ public class UserIpv6AddressDaoImpl extends GenericDaoBase listByVlanIdAndState(long vlanId, IpAddress.State state) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vlan", vlanId); + sc.setParameters("state", state); + return listBy(sc); + } + @Override public List listByDcId(long dcId) { SearchCriteria sc = AllFieldsSearch.create(); diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index 773f168b89b..5a0a34baa10 100755 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -35,10 +36,12 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; +import java.util.Vector; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.googlecode.ipv6.IPv6Address; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupService; @@ -69,6 +72,7 @@ import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.UpdateVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; @@ -167,6 +171,7 @@ import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.host.dao.HostTagsDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.IpAddress; import com.cloud.network.IpAddressManager; import com.cloud.network.Network; import com.cloud.network.Network.Capability; @@ -178,6 +183,7 @@ import com.cloud.network.NetworkService; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetwork; +import com.cloud.network.UserIpv6AddressVO; import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; @@ -187,6 +193,7 @@ import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao; import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO; import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.dao.UserIpv6AddressDao; import com.cloud.network.rules.LoadBalancerContainer.Scheme; import com.cloud.network.vpc.VpcManager; import com.cloud.offering.DiskOffering; @@ -403,7 +410,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati StoragePoolTagsDao storagePoolTagDao; @Inject private AnnotationDao annotationDao; - + @Inject + UserIpv6AddressDao _ipv6Dao; // FIXME - why don't we have interface for DataCenterLinkLocalIpAddressDao? @Inject @@ -4239,6 +4247,260 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } + @Override + public Vlan updateVlanAndPublicIpRange(UpdateVlanIpRangeCmd cmd) throws ConcurrentOperationException, + ResourceUnavailableException,ResourceAllocationException { + + return updateVlanAndPublicIpRange(cmd.getId(), cmd.getStartIp(),cmd.getEndIp(), cmd.getGateway(),cmd.getNetmask(), + cmd.getStartIpv6(), cmd.getEndIpv6(), cmd.getIp6Gateway(), cmd.getIp6Cidr(), cmd.isForSystemVms()); + } + + @DB + @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_UPDATE, eventDescription = "update vlan ip Range", async + = false) + public Vlan updateVlanAndPublicIpRange(final long id, String startIp, + String endIp, + String gateway, + String netmask, + String startIpv6, + String endIpv6, + String ip6Gateway, + String ip6Cidr, + Boolean forSystemVms) throws ConcurrentOperationException { + + VlanVO vlanRange = _vlanDao.findById(id); + if (vlanRange == null) { + throw new InvalidParameterValueException("Please specify a valid IP range id."); + } + + final boolean ipv4 = vlanRange.getVlanGateway() != null; + final boolean ipv6 = vlanRange.getIp6Gateway() != null; + if (!ipv4) { + if (startIp != null || endIp != null || gateway != null || netmask != null) { + throw new InvalidParameterValueException("IPv4 is not support in this IP range."); + } + } + if (!ipv6) { + if (startIpv6 != null || endIpv6 != null || ip6Gateway != null || ip6Cidr != null) { + throw new InvalidParameterValueException("IPv6 is not support in this IP range."); + } + } + + final Boolean isRangeForSystemVM = checkIfVlanRangeIsForSystemVM(id); + if (forSystemVms != null && isRangeForSystemVM != forSystemVms) { + if (VlanType.DirectAttached.equals(vlanRange.getVlanType())) { + throw new InvalidParameterValueException("forSystemVms is not available for this IP range with vlan type: " + VlanType.DirectAttached); + } + // Check if range has already been dedicated + final List maps = _accountVlanMapDao.listAccountVlanMapsByVlan(id); + if (maps != null && !maps.isEmpty()) { + throw new InvalidParameterValueException("Specified Public IP range has already been dedicated to an account"); + } + + List domainmaps = _domainVlanMapDao.listDomainVlanMapsByVlan(id); + if (domainmaps != null && !domainmaps.isEmpty()) { + throw new InvalidParameterValueException("Specified Public IP range has already been dedicated to a domain"); + } + } + if (ipv4) { + updateVlanAndIpv4Range(id, vlanRange, startIp, endIp, gateway, netmask, isRangeForSystemVM, forSystemVms); + } + if (ipv6) { + updateVlanAndIpv6Range(id, vlanRange, startIpv6, endIpv6, ip6Gateway, ip6Cidr, isRangeForSystemVM, forSystemVms); + } + return _vlanDao.findById(id); + } + + private void updateVlanAndIpv4Range(final long id, final VlanVO vlanRange, + String startIp, + String endIp, + String gateway, + String netmask, + final Boolean isRangeForSystemVM, + final Boolean forSystemVms) { + final List listAllocatedIPs = _publicIpAddressDao.listByVlanIdAndState(id, IpAddress.State.Allocated); + + if (gateway != null && !gateway.equals(vlanRange.getVlanGateway()) && CollectionUtils.isNotEmpty(listAllocatedIPs)) { + throw new InvalidParameterValueException(String.format("Unable to change gateway to %s because some IPs are in use", gateway)); + } + if (netmask != null && !netmask.equals(vlanRange.getVlanNetmask()) && CollectionUtils.isNotEmpty(listAllocatedIPs)) { + throw new InvalidParameterValueException(String.format("Unable to change netmask to %s because some IPs are in use", netmask)); + } + + gateway = MoreObjects.firstNonNull(gateway, vlanRange.getVlanGateway()); + netmask = MoreObjects.firstNonNull(netmask, vlanRange.getVlanNetmask()); + + final String[] existingVlanIPRangeArray = vlanRange.getIpRange().split("-"); + final String currentStartIP = existingVlanIPRangeArray[0]; + final String currentEndIP = existingVlanIPRangeArray[1]; + + startIp = MoreObjects.firstNonNull(startIp, currentStartIP); + endIp = MoreObjects.firstNonNull(endIp, currentEndIP); + + final String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask); + if (Strings.isNullOrEmpty(cidr)) { + throw new InvalidParameterValueException(String.format("Invalid gateway (%s) or netmask (%s)", gateway, netmask)); + } + final String cidrAddress = getCidrAddress(cidr); + final long cidrSize = getCidrSize(cidr); + + checkIpRange(startIp, endIp, cidrAddress, cidrSize); + + checkGatewayOverlap(startIp, endIp, gateway); + + checkAllocatedIpsAreWithinVlanRange(listAllocatedIPs, startIp, endIp, forSystemVms); + + try { + final String newStartIP = startIp; + final String newEndIP = endIp; + + VlanVO range = _vlanDao.acquireInLockTable(id, 30); + if (range == null) { + throw new CloudRuntimeException("Unable to acquire vlan configuration: " + id); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("lock vlan " + id + " is acquired"); + } + + commitUpdateVlanAndIpRange(id, newStartIP, newEndIP, currentStartIP, currentEndIP, gateway, netmask,true, isRangeForSystemVM, forSystemVms); + + } catch (final Exception e) { + s_logger.error("Unable to edit VlanRange due to " + e.getMessage(), e); + throw new CloudRuntimeException("Failed to edit VlanRange. Please contact Cloud Support."); + } finally { + _vlanDao.releaseFromLockTable(id); + } + } + + private void updateVlanAndIpv6Range(final long id, final VlanVO vlanRange, + String startIpv6, + String endIpv6, + String ip6Gateway, + String ip6Cidr, + final Boolean isRangeForSystemVM, + final Boolean forSystemVms) { + final List listAllocatedIPs = _ipv6Dao.listByVlanIdAndState(id, IpAddress.State.Allocated); + + if (ip6Gateway != null && !ip6Gateway.equals(vlanRange.getIp6Gateway()) && CollectionUtils.isNotEmpty(listAllocatedIPs)) { + throw new InvalidParameterValueException(String.format("Unable to change ipv6 gateway to %s because some IPs are in use", ip6Gateway)); + } + if (ip6Cidr != null && !ip6Cidr.equals(vlanRange.getIp6Cidr()) && CollectionUtils.isNotEmpty(listAllocatedIPs)) { + throw new InvalidParameterValueException(String.format("Unable to change ipv6 cidr to %s because some IPs are in use", ip6Cidr)); + } + ip6Gateway = MoreObjects.firstNonNull(ip6Gateway, vlanRange.getIp6Gateway()); + ip6Cidr = MoreObjects.firstNonNull(ip6Cidr, vlanRange.getIp6Cidr()); + + final String[] existingVlanIPRangeArray = vlanRange.getIp6Range().split("-"); + final String currentStartIPv6 = existingVlanIPRangeArray[0]; + final String currentEndIPv6 = existingVlanIPRangeArray[1]; + + startIpv6 = MoreObjects.firstNonNull(startIpv6, currentStartIPv6); + endIpv6 = MoreObjects.firstNonNull(endIpv6, currentEndIPv6); + + if (startIpv6 != currentStartIPv6 || endIpv6 != currentEndIPv6) { + _networkModel.checkIp6Parameters(startIpv6, endIpv6, ip6Gateway, ip6Cidr); + checkAllocatedIpv6sAreWithinVlanRange(listAllocatedIPs, startIpv6, endIpv6); + + try { + VlanVO range = _vlanDao.acquireInLockTable(id, 30); + if (range == null) { + throw new CloudRuntimeException("Unable to acquire vlan configuration: " + id); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("lock vlan " + id + " is acquired"); + } + + commitUpdateVlanAndIpRange(id, startIpv6, endIpv6, currentStartIPv6, currentEndIPv6, ip6Gateway, ip6Cidr, false, isRangeForSystemVM,forSystemVms); + + } catch (final Exception e) { + s_logger.error("Unable to edit VlanRange due to " + e.getMessage(), e); + throw new CloudRuntimeException("Failed to edit VlanRange. Please contact Cloud Support."); + } finally { + _vlanDao.releaseFromLockTable(id); + } + } + } + + private VlanVO commitUpdateVlanAndIpRange(final Long id, final String newStartIP, final String newEndIP, final String currentStartIP, final String currentEndIP, + final String gateway, final String netmask, + final boolean ipv4, final Boolean isRangeForSystemVM, final Boolean forSystemvms) { + + return Transaction.execute(new TransactionCallback() { + @Override + public VlanVO doInTransaction(final TransactionStatus status) { + VlanVO vlanRange = _vlanDao.findById(id); + s_logger.debug("Updating vlan range " + vlanRange.getId()); + if (ipv4) { + vlanRange.setIpRange(newStartIP + "-" + newEndIP); + vlanRange.setVlanGateway(gateway); + vlanRange.setVlanNetmask(netmask); + _vlanDao.update(vlanRange.getId(), vlanRange); + if (!updatePublicIPRange(newStartIP, currentStartIP, newEndIP, currentEndIP, vlanRange.getDataCenterId(), vlanRange.getId(), vlanRange.getNetworkId(), vlanRange.getPhysicalNetworkId(), isRangeForSystemVM, forSystemvms)) { + throw new CloudRuntimeException("Failed to update IPv4 range. Please contact Cloud Support."); + } + } else { + vlanRange.setIp6Range(newStartIP + "-" + newEndIP); + vlanRange.setIp6Gateway(gateway); + vlanRange.setIp6Cidr(netmask); + _vlanDao.update(vlanRange.getId(), vlanRange); + } + return vlanRange; + } + }); + } + + private boolean checkIfVlanRangeIsForSystemVM(final long vlanId) { + List existingPublicIPs = _publicIpAddressDao.listByVlanId(vlanId); + boolean initialIsSystemVmValue = existingPublicIPs.get(0).isForSystemVms(); + for (IPAddressVO existingIPs : existingPublicIPs) { + if (initialIsSystemVmValue != existingIPs.isForSystemVms()) { + throw new CloudRuntimeException("Your \"For System VM\" value seems to be inconsistent with the rest of the records. Please contact Cloud Support"); + } + } + return initialIsSystemVmValue; + } + + private void checkAllocatedIpsAreWithinVlanRange + (List listAllocatedIPs, String startIp, String endIp, Boolean forSystemVms) { + Collections.sort(listAllocatedIPs, Comparator.comparing(IPAddressVO::getAddress)); + for (IPAddressVO allocatedIP : listAllocatedIPs) { + if ((!Strings.isNullOrEmpty(startIp) && NetUtils.ip2Long(startIp) > NetUtils.ip2Long(allocatedIP.getAddress().addr())) + || (!Strings.isNullOrEmpty(endIp) && NetUtils.ip2Long(endIp) < NetUtils.ip2Long(allocatedIP.getAddress().addr()))) { + throw new InvalidParameterValueException(String.format("The start IP address must be less than or equal to %s which is already in use. " + + "The end IP address must be greater than or equal to %s which is already in use. " + + "There are %d IPs already allocated in this range.", + listAllocatedIPs.get(0).getAddress(), listAllocatedIPs.get(listAllocatedIPs.size() - 1).getAddress(), listAllocatedIPs.size())); + } + if (forSystemVms != null && allocatedIP.isForSystemVms() != forSystemVms) { + throw new InvalidParameterValueException(String.format("IP %s is in use, cannot change forSystemVms of the IP range", allocatedIP.getAddress().addr())); + } + } + } + + private void checkAllocatedIpv6sAreWithinVlanRange(List listAllocatedIPs, String startIpv6, String endIpv6) { + Collections.sort(listAllocatedIPs, Comparator.comparing(UserIpv6AddressVO::getAddress)); + for (UserIpv6AddressVO allocatedIP : listAllocatedIPs) { + if ((!Strings.isNullOrEmpty(startIpv6) + && IPv6Address.fromString(startIpv6).toBigInteger().compareTo(IPv6Address.fromString(allocatedIP.getAddress()).toBigInteger()) > 0) + || (!Strings.isNullOrEmpty(endIpv6) + && IPv6Address.fromString(endIpv6).toBigInteger().compareTo(IPv6Address.fromString(allocatedIP.getAddress()).toBigInteger()) < 0)) { + throw new InvalidParameterValueException(String.format("The start IPv6 address must be less than or equal to %s which is already in use. " + + "The end IPv6 address must be greater than or equal to %s which is already in use. " + + "There are %d IPv6 addresses already allocated in this range.", + listAllocatedIPs.get(0).getAddress(), listAllocatedIPs.get(listAllocatedIPs.size() - 1).getAddress(), listAllocatedIPs.size())); + } + } + } + + private void checkGatewayOverlap(String startIp, String endIp, String gateway) { + if (NetUtils.ipRangesOverlap(startIp, endIp, gateway, gateway)) { + throw new InvalidParameterValueException("The gateway shouldn't overlap the new start/end ip " + + "addresses"); + } + } + @Override @DB public boolean deleteVlanAndPublicIpRange(final long userId, final long vlanDbId, final Account caller) { @@ -4292,7 +4554,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + " as ip " + ip + " belonging to the range has firewall rules applied. Cleanup the rules first"); } - if (ip.getAllocatedTime() != null) {// This means IP is allocated + if (ip.getAllocatedTime() != null) { + // This means IP is allocated // release public ip address here success = _ipAddrMgr.disassociatePublicIpAddress(ip.getId(), userId, caller); } @@ -4568,6 +4831,59 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } }); + return CollectionUtils.isEmpty(problemIps); + } + + @DB + protected boolean updatePublicIPRange(final String newStartIP, final String currentStartIP, final String newEndIP, final String currentEndIP, final long zoneId, final long vlanDbId, final long sourceNetworkid, final long physicalNetworkId, final boolean isRangeForSystemVM, final Boolean forSystemVms) { + long newStartIPLong = NetUtils.ip2Long(newStartIP); + long newEndIPLong = NetUtils.ip2Long(newEndIP); + long currentStartIPLong = NetUtils.ip2Long(currentStartIP); + long currentEndIPLong = NetUtils.ip2Long(currentEndIP); + + List currentIPRange = new ArrayList<>(); + List newIPRange = new ArrayList<>(); + while (newStartIPLong <= newEndIPLong) { + newIPRange.add(newStartIPLong); + newStartIPLong++; + } + while (currentStartIPLong <= currentEndIPLong) { + currentIPRange.add(currentStartIPLong); + currentStartIPLong++; + } + + final List problemIps = Transaction.execute(new TransactionCallback>() { + + @Override + public List doInTransaction(final TransactionStatus status) { + final IPRangeConfig config = new IPRangeConfig(); + Vector configResult = new Vector<>(); + List ipAddressesToAdd = new ArrayList(newIPRange); + ipAddressesToAdd.removeAll(currentIPRange); + if (ipAddressesToAdd.size() > 0) { + for (Long startIP : ipAddressesToAdd) { + configResult.addAll(config.savePublicIPRange(TransactionLegacy.currentTxn(), startIP, startIP, zoneId, vlanDbId, sourceNetworkid, physicalNetworkId, forSystemVms != null ? forSystemVms : isRangeForSystemVM)); + } + } + List ipAddressesToDelete = new ArrayList(currentIPRange); + ipAddressesToDelete.removeAll(newIPRange); + if (ipAddressesToDelete.size() > 0) { + for (Long startIP : ipAddressesToDelete) { + configResult.addAll(config.deletePublicIPRange(TransactionLegacy.currentTxn(), startIP, startIP, vlanDbId)); + } + } + if (forSystemVms != null && isRangeForSystemVM != forSystemVms) { + List ipAddressesToUpdate = new ArrayList(currentIPRange); + ipAddressesToUpdate.removeAll(ipAddressesToDelete); + if (ipAddressesToUpdate.size() > 0) { + for (Long startIP : ipAddressesToUpdate) { + configResult.addAll(config.updatePublicIPRange(TransactionLegacy.currentTxn(), startIP, startIP, vlanDbId, forSystemVms)); + } + } + } + return configResult; + } + }); return problemIps != null && problemIps.size() == 0; } diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index b27ad8ce3d7..49f45d9e3ff 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -262,6 +262,7 @@ import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.ListVlanIpRangesCmd; import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.UpdateVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.vm.AddNicToVMCmdByAdmin; import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd; import org.apache.cloudstack.api.command.admin.vm.DeployVMCmdByAdmin; @@ -3105,6 +3106,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(RegisterCmd.class); cmdList.add(UpdateUserCmd.class); cmdList.add(CreateVlanIpRangeCmd.class); + cmdList.add(UpdateVlanIpRangeCmd.class); cmdList.add(DeleteVlanIpRangeCmd.class); cmdList.add(ListVlanIpRangesCmd.class); cmdList.add(DedicatePublicIpRangeCmd.class); diff --git a/server/src/main/java/com/cloud/test/IPRangeConfig.java b/server/src/main/java/com/cloud/test/IPRangeConfig.java index f35989102dd..22ebb56963a 100644 --- a/server/src/main/java/com/cloud/test/IPRangeConfig.java +++ b/server/src/main/java/com/cloud/test/IPRangeConfig.java @@ -313,9 +313,8 @@ public class IPRangeConfig { return problemIPs; } - private Vector deletePublicIPRange(TransactionLegacy txn, long startIP, long endIP, long vlanDbId) { - String deleteSql = "DELETE FROM `cloud`.`user_ip_address` WHERE public_ip_address = ? AND vlan_id = ?"; - String isPublicIPAllocatedSelectSql = "SELECT * FROM `cloud`.`user_ip_address` WHERE public_ip_address = ? AND vlan_id = ?"; + public Vector updatePublicIPRange(TransactionLegacy txn, long startIP, long endIP, long vlanDbId, boolean forSystemvms) { + String updateSql = "UPDATE `cloud`.`user_ip_address` SET forsystemvms = ? WHERE public_ip_address = ? AND vlan_db_id = ?"; Vector problemIPs = new Vector(); Connection conn = null; @@ -323,25 +322,55 @@ public class IPRangeConfig { conn = txn.getConnection(); } catch (SQLException e) { - System.out.println("deletePublicIPRange. Exception: " +e.getMessage()); + System.out.println("updatePublicIPRange. Exception: " + e.getMessage()); return null; } - try(PreparedStatement stmt = conn.prepareStatement(deleteSql); + try (PreparedStatement stmt = conn.prepareStatement(updateSql);) { + while (startIP <= endIP) { + stmt.clearParameters(); + stmt.setBoolean(1, forSystemvms); + stmt.setString(2, NetUtils.long2Ip(startIP)); + stmt.setLong(3, vlanDbId); + stmt.executeUpdate(); + startIP += 1; + } + } catch (Exception ex) { + System.out.println("updatePublicIPRange. Exception: " + ex.getMessage()); + return null; + } + + return problemIPs; + } + + public Vector deletePublicIPRange(TransactionLegacy txn, long startIP, long endIP, long vlanDbId) { + String deleteSql = "DELETE FROM `cloud`.`user_ip_address` WHERE public_ip_address = ? AND vlan_db_id = ?"; + String isPublicIPAllocatedSelectSql = "SELECT * FROM `cloud`.`user_ip_address` WHERE public_ip_address = ? AND vlan_db_id = ?"; + + Vector problemIPs = new Vector(); + Connection conn = null; + try { + conn = txn.getConnection(); + } + catch (SQLException e) { + System.out.println("deletePublicIPRange. Exception: " + e.getMessage()); + return null; + } + try (PreparedStatement stmt = conn.prepareStatement(deleteSql); PreparedStatement isAllocatedStmt = conn.prepareStatement(isPublicIPAllocatedSelectSql);) { while (startIP <= endIP) { - if (!isPublicIPAllocated(startIP, vlanDbId, isAllocatedStmt)) { + if (!isPublicIPAllocated(NetUtils.long2Ip(startIP), vlanDbId, isAllocatedStmt)) { stmt.clearParameters(); - stmt.setLong(1, startIP); + stmt.setString(1, NetUtils.long2Ip(startIP)); stmt.setLong(2, vlanDbId); stmt.executeUpdate(); - } + } else { problemIPs.add(NetUtils.long2Ip(startIP)); } startIP += 1; } - }catch (Exception ex) { - System.out.println("deletePublicIPRange. Exception: " +ex.getMessage()); + } catch (Exception ex) { + System.out.println("deletePublicIPRange. Exception: " + ex.getMessage()); return null; } @@ -372,28 +401,33 @@ public class IPRangeConfig { System.out.println("deletePrivateIPRange. Exception: " + e.getMessage()); printError("deletePrivateIPRange. Exception: " + e.getMessage()); } - }catch (SQLException e) { + } catch (SQLException e) { System.out.println("deletePrivateIPRange. Exception: " + e.getMessage()); printError("deletePrivateIPRange. Exception: " + e.getMessage()); } return problemIPs; } - private boolean isPublicIPAllocated(long ip, long vlanDbId, PreparedStatement stmt) { - try(ResultSet rs = stmt.executeQuery();) { - stmt.clearParameters(); - stmt.setLong(1, ip); - stmt.setLong(2, vlanDbId); - if (rs.next()) { - return (rs.getString("allocated") != null); - } else { - return false; - } - } - catch (SQLException ex) { - System.out.println(ex.getMessage()); - return true; - } + private boolean isPublicIPAllocated(String ip, long vlanDbId, PreparedStatement stmt) { + try { + stmt.clearParameters(); + stmt.setString(1, ip); + stmt.setLong(2, vlanDbId); + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + return (rs.getString("allocated") != null); + } else { + return false; + } + } + catch (SQLException ex) { + System.out.println(ex.getMessage()); + return true; + } + } catch (SQLException ex) { + System.out.println(ex.getMessage()); + return true; + } } private boolean isPrivateIPAllocated(String ip, long podId, long zoneId, PreparedStatement stmt) { diff --git a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java index 9618f8d0316..d6dbee844b1 100644 --- a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -44,6 +44,7 @@ import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DedicatePublicIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.vlan.ReleasePublicIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.UpdateVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; @@ -258,6 +259,17 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu return null; } + /* (non-Javadoc) + * @see com.cloud.configuration.ConfigurationService#updateVlanAndPublicIpRange(org.apache.cloudstack.api + * .commands.UpdateVlanIpRangeCmd) + */ + @Override + public Vlan updateVlanAndPublicIpRange(UpdateVlanIpRangeCmd cmd) throws ConcurrentOperationException, + ResourceUnavailableException, ResourceAllocationException{ + // TODO Auto-generated method stub + return null; + } + /* (non-Javadoc) * @see com.cloud.configuration.ConfigurationService#markDefaultZone(java.lang.String, long, long) */ diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 588d6a33111..f7230ca1681 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -2240,6 +2240,7 @@ "label.unmanaged.instances": "Unmanaged Instances", "label.untagged": "Untagged", "label.update.instance.group": "Update Instance Group", +"label.update.ip.range": "Update IP range", "label.update.physical.network": "Update Physical Network", "label.update.project.resources": "Update project resources", "label.update.project.role": "Update project role", @@ -3288,6 +3289,7 @@ "message.success.scale.kubernetes": "Successfully scaled Kubernetes cluster", "message.success.unmanage.instance": "Successfully unmanaged instance", "message.success.update.ipaddress": "Successfully updated IP Address", +"message.success.update.iprange": "Successfully updated IP range", "message.success.update.kubeversion": "Successfully updated Kubernetes supported version", "message.success.update.user": "Successfully updated user", "message.success.upgrade.kubernetes": "Successfully upgraded Kubernetes cluster", diff --git a/ui/src/views/infra/network/IpRangesTabPublic.vue b/ui/src/views/infra/network/IpRangesTabPublic.vue index c796524bf4f..1e4e03c8804 100644 --- a/ui/src/views/infra/network/IpRangesTabPublic.vue +++ b/ui/src/views/infra/network/IpRangesTabPublic.vue @@ -54,6 +54,13 @@ type="danger" @click="() => handleRemoveAccount(record.id)" :disabled="!('releasePublicIpRange' in $store.getters.apis)" /> + + + + + + + + + + + + + + + + + + + + + + + +
+ {{ $t('label.cancel') }} + {{ $t('label.ok') }} +
+
+
+ @@ -280,6 +333,7 @@ export default { domains: [], domainsLoading: false, addIpRangeModal: false, + updateIpRangeModal: false, showAccountFields: false, podsLoading: false, pods: [], @@ -443,6 +497,10 @@ export default { handleOpenAddIpRangeModal () { this.addIpRangeModal = true }, + handleUpdateIpRangeModal (item) { + this.selectedItem = item + this.updateIpRangeModal = true + }, handleDeleteIpRange (id) { this.componentLoading = true api('deleteVlanIpRange', { id }).then(() => { @@ -498,6 +556,38 @@ export default { }) }) }, + handleUpdateIpRange (e) { + if (this.componentLoading) return + this.form.validateFields((error, values) => { + if (error) return + + this.componentLoading = true + this.updateIpRangeModal = false + var params = { + id: this.selectedItem.id, + gateway: values.gateway, + netmask: values.netmask, + startip: values.startip, + endip: values.endip, + forsystemvms: values.forsystemvms + } + api('updateVlanIpRange', params).then(() => { + this.$notification.success({ + message: this.$t('message.success.update.iprange') + }) + }).catch(error => { + this.$notification.error({ + message: `${this.$t('label.error')} ${error.response.status}`, + description: error.response.data.updatevlaniprangeresponse + ? error.response.data.updatevlaniprangeresponse.errortext : error.response.data.errorresponse.errortext, + duration: 0 + }) + }).finally(() => { + this.componentLoading = false + this.fetchData() + }) + }) + }, changePage (page, pageSize) { this.page = page this.pageSize = pageSize