diff --git a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java index ebbae0b31c2..0aff8435d9e 100644 --- a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java +++ b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import com.cloud.dc.VlanVO; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.impl.ConfigurationSubGroupVO; @@ -189,7 +190,7 @@ public interface ConfigurationManager { * @param caller * @return success/failure */ - boolean deleteVlanAndPublicIpRange(long userId, long vlanDbId, Account caller); + VlanVO deleteVlanAndPublicIpRange(long userId, long vlanDbId, Account caller); void checkZoneAccess(Account caller, DataCenter zone); diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index d07fee32276..fded0a38dde 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -259,6 +259,8 @@ import com.cloud.vm.dao.VMInstanceDao; import com.googlecode.ipv6.IPv6Address; import org.jetbrains.annotations.NotNull; +import static com.cloud.configuration.ConfigurationManager.MESSAGE_DELETE_VLAN_IP_RANGE_EVENT; + /** * NetworkManagerImpl implements NetworkManager. */ @@ -3324,17 +3326,17 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra final NetworkVO networkFinal = network; try { - Transaction.execute(new TransactionCallbackNoReturn() { + final List deletedVlanRangeToPublish = Transaction.execute(new TransactionCallback>() { @Override - public void doInTransactionWithoutResult(final TransactionStatus status) { + public List doInTransaction(TransactionStatus status) { final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, networkFinal.getGuruName()); if (!guru.trash(networkFinal, _networkOfferingDao.findById(networkFinal.getNetworkOfferingId()))) { throw new CloudRuntimeException("Failed to trash network."); } - - if (!deleteVlansInNetwork(networkFinal, context.getCaller().getId(), callerAccount)) { - logger.warn("Failed to delete network {}; was unable to cleanup corresponding ip ranges", networkFinal); + Pair> deletedVlans = deleteVlansInNetwork(networkFinal, context.getCaller().getId(), callerAccount); + if (!deletedVlans.first()) { + logger.warn("Failed to delete network " + networkFinal + "; was unable to cleanup corresponding ip ranges"); throw new CloudRuntimeException("Failed to delete network " + networkFinal + "; was unable to cleanup corresponding ip ranges"); } else { // commit transaction only when ips and vlans for the network are released successfully @@ -3367,8 +3369,10 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra _resourceLimitMgr.decrementResourceCount(networkFinal.getAccountId(), ResourceType.network, networkFinal.getDisplayNetwork()); } } + return deletedVlans.second(); } }); + publishDeletedVlanRanges(deletedVlanRangeToPublish); if (_networksDao.findById(network.getId()) == null) { // remove its related ACL permission final Pair, Long> networkMsg = new Pair, Long>(Network.class, networkFinal.getId()); @@ -3386,6 +3390,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return success; } + private void publishDeletedVlanRanges(List deletedVlanRangeToPublish) { + if (CollectionUtils.isNotEmpty(deletedVlanRangeToPublish)) { + for (VlanVO vlan : deletedVlanRangeToPublish) { + _messageBus.publish(_name, MESSAGE_DELETE_VLAN_IP_RANGE_EVENT, PublishScope.LOCAL, vlan); + } + } + } + @Override public boolean resourceCountNeedsUpdate(final NetworkOffering ntwkOff, final ACLType aclType) { //Update resource count only for Isolated account specific non-system networks @@ -3393,15 +3405,19 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return updateResourceCount; } - protected boolean deleteVlansInNetwork(final NetworkVO network, final long userId, final Account callerAccount) { + protected Pair> deleteVlansInNetwork(final NetworkVO network, final long userId, final Account callerAccount) { final long networkId = network.getId(); //cleanup Public vlans final List publicVlans = _vlanDao.listVlansByNetworkId(networkId); + List deletedPublicVlanRange = new ArrayList<>(); boolean result = true; for (final VlanVO vlan : publicVlans) { - if (!_configMgr.deleteVlanAndPublicIpRange(userId, vlan.getId(), callerAccount)) { - logger.warn("Failed to delete vlan {});", vlan.getId()); + VlanVO vlanRange = _configMgr.deleteVlanAndPublicIpRange(userId, vlan.getId(), callerAccount); + if (vlanRange == null) { + logger.warn("Failed to delete vlan " + vlan.getId() + ");"); result = false; + } else { + deletedPublicVlanRange.add(vlanRange); } } @@ -3421,7 +3437,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra _dcDao.releaseVnet(BroadcastDomainType.getValue(network.getBroadcastUri()), network.getDataCenterId(), network.getPhysicalNetworkId(), network.getAccountId(), network.getReservationId()); } - return result; + return new Pair<>(result, deletedPublicVlanRange); } public class NetworkGarbageCollector extends ManagedContextRunnable { diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index a5f11900080..8f68af80366 100644 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -5362,7 +5362,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @DB - public boolean deleteVlanAndPublicIpRange(final long userId, final long vlanDbId, final Account caller) { + public VlanVO deleteVlanAndPublicIpRange(final long userId, final long vlanDbId, final Account caller) { VlanVO vlanRange = _vlanDao.findById(vlanDbId); if (vlanRange == null) { throw new InvalidParameterValueException("Please specify a valid IP range id."); @@ -5468,9 +5468,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } }); - messageBus.publish(_name, MESSAGE_DELETE_VLAN_IP_RANGE_EVENT, PublishScope.LOCAL, vlanRange); - - return true; + return vlanRange; } @Override @@ -5976,7 +5974,16 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Please specify a valid IP range id."); } - return deleteVlanAndPublicIpRange(CallContext.current().getCallingUserId(), vlanDbId, CallContext.current().getCallingAccount()); + return deleteAndPublishVlanAndPublicIpRange(CallContext.current().getCallingUserId(), vlanDbId, CallContext.current().getCallingAccount()); + } + + private boolean deleteAndPublishVlanAndPublicIpRange(final long userId, final long vlanDbId, final Account caller) { + VlanVO deletedVlan = deleteVlanAndPublicIpRange(userId, vlanDbId, caller); + if (deletedVlan != null) { + messageBus.publish(_name, MESSAGE_DELETE_VLAN_IP_RANGE_EVENT, PublishScope.LOCAL, deletedVlan); + return true; + } + return false; } @Override diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index dce8a21b478..a5a8f07546b 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -8044,7 +8044,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // Detach, destroy and create the usage event for the old root volume. _volsDao.detachVolume(root.getId()); - _volumeService.destroyVolume(root.getId(), caller, Volume.State.Allocated.equals(root.getState()) || expunge, false); + destroyVolumeInContext(vm, Volume.State.Allocated.equals(root.getState()) || expunge, root); if (currentTemplate.getId() != template.getId() && VirtualMachine.Type.User.equals(vm.type) && !VirtualMachineManager.ResourceCountRunningVMsonly.value()) { ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); diff --git a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java index 14e65634947..2a1f4fffbf8 100644 --- a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -26,6 +26,7 @@ import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.Pod; import com.cloud.dc.Vlan; +import com.cloud.dc.VlanVO; import com.cloud.domain.Domain; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; @@ -515,9 +516,9 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu * @see com.cloud.configuration.ConfigurationManager#deleteVlanAndPublicIpRange(long, long, com.cloud.user.Account) */ @Override - public boolean deleteVlanAndPublicIpRange(long userId, long vlanDbId, Account caller) { + public VlanVO deleteVlanAndPublicIpRange(long userId, long vlanDbId, Account caller) { // TODO Auto-generated method stub - return false; + return null; } /* (non-Javadoc) diff --git a/test/integration/smoke/test_events_resource.py b/test/integration/smoke/test_events_resource.py index 660cbd37bce..79443110950 100644 --- a/test/integration/smoke/test_events_resource.py +++ b/test/integration/smoke/test_events_resource.py @@ -116,6 +116,7 @@ class TestEventsResource(cloudstackTestCase): self.services["domain"], parentdomainid=self.domain.id ) + self.cleanup.append(domain1) self.services["domainid"] = domain1.id account = Account.create( @@ -123,6 +124,7 @@ class TestEventsResource(cloudstackTestCase): self.services["account"], domainid=domain1.id ) + self.cleanup.append(account) account_network = Network.create( self.apiclient, @@ -130,6 +132,7 @@ class TestEventsResource(cloudstackTestCase): account.name, account.domainid ) + self.cleanup.append(account_network) virtual_machine = VirtualMachine.create( self.apiclient, self.services, @@ -138,6 +141,7 @@ class TestEventsResource(cloudstackTestCase): networkids=account_network.id, serviceofferingid=self.service_offering.id ) + self.cleanup.append(virtual_machine) volume = Volume.create( self.apiclient, self.services, @@ -146,6 +150,7 @@ class TestEventsResource(cloudstackTestCase): domainid=account.domainid, diskofferingid=self.disk_offering.id ) + self.cleanup.append(volume) virtual_machine.attach_volume( self.apiclient, volume @@ -157,15 +162,20 @@ class TestEventsResource(cloudstackTestCase): time.sleep(self.services["sleep"]) virtual_machine.detach_volume(self.apiclient, volume) volume.delete(self.apiclient) + self.cleanup.remove(volume) ts = str(time.time()) virtual_machine.update(self.apiclient, displayname=ts) virtual_machine.delete(self.apiclient) + self.cleanup.remove(virtual_machine) account_network.update(self.apiclient, name=account_network.name + ts) account_network.delete(self.apiclient) + self.cleanup.remove(account_network) account.update(self.apiclient, newname=account.name + ts) account.disable(self.apiclient) account.delete(self.apiclient) + self.cleanup.remove(account) domain1.delete(self.apiclient) + self.cleanup.remove(domain1) cmd = listEvents.listEventsCmd() cmd.startdate = start_time @@ -185,8 +195,9 @@ class TestEventsResource(cloudstackTestCase): for event in events: if event.type.startswith("VM.") or (event.type.startswith("NETWORK.") and not event.type.startswith("NETWORK.ELEMENT")) or event.type.startswith("VOLUME.") or event.type.startswith("ACCOUNT.") or event.type.startswith("DOMAIN.") or event.type.startswith("TEMPLATE."): if event.resourceid is None or event.resourcetype is None: - self.debug("Failed event:: %s" % json.dumps(event, indent=2)) - self.fail("resourceid or resourcetype for the event not found!") + event_json = json.dumps(event.__dict__, indent=2) + self.debug("Failed event:: %s" % event_json) + self.fail("resourceid or resourcetype not found for the event: %s" % event_json) else: self.debug("Event %s at %s:: Resource Type: %s, Resource ID: %s" % (event.type, event.created, event.resourcetype, event.resourceid)) diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 7a0644ba98f..ba3a21e1539 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -626,7 +626,7 @@ export default { name: 'autoscalevmgroup', title: 'label.autoscale.vm.groups', icon: 'fullscreen-outlined', - docHelp: 'adminguide/autoscale_without_netscaler.html', + docHelp: 'adminguide/autoscale_with_virtual_router.html', resourceType: 'AutoScaleVmGroup', permission: ['listAutoScaleVmGroups'], columns: (store) => { diff --git a/ui/src/views/compute/EditVM.vue b/ui/src/views/compute/EditVM.vue index 3601901252b..550c4645ed6 100644 --- a/ui/src/views/compute/EditVM.vue +++ b/ui/src/views/compute/EditVM.vue @@ -177,7 +177,8 @@ export default { isdynamicallyscalable: this.resource.isdynamicallyscalable, group: this.resource.group, securitygroupids: this.resource.securitygroup.map(x => x.id), - userdata: '' + userdata: '', + haenable: this.resource.haenable }) this.rules = reactive({}) }, diff --git a/ui/src/views/dashboard/UsageDashboard.vue b/ui/src/views/dashboard/UsageDashboard.vue index e9edd0cfb6e..fe835cbd0d0 100644 --- a/ui/src/views/dashboard/UsageDashboard.vue +++ b/ui/src/views/dashboard/UsageDashboard.vue @@ -202,7 +202,7 @@ @@ -238,7 +238,7 @@ @@ -274,7 +274,7 @@