diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java index c01c1650fc0..95195372ee9 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java @@ -76,6 +76,8 @@ public interface VMTemplateDao extends GenericDao, StateDao< public Long countTemplatesForAccount(long accountId); + public List listUnRemovedTemplatesByStates(VirtualMachineTemplate.State ...states); + List findTemplatesToSyncToS3(); void loadDetails(VMTemplateVO tmpl); diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java index a3bebfa332f..79954307d44 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java @@ -106,6 +106,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem // private SearchBuilder updateStateSearch; private SearchBuilder AllFieldsSearch; protected SearchBuilder ParentTemplateIdSearch; + private SearchBuilder InactiveUnremovedTmpltSearch; @Inject ResourceTagDao _tagsDao; @@ -431,6 +432,11 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem ParentTemplateIdSearch.and("state", ParentTemplateIdSearch.entity().getState(), SearchCriteria.Op.EQ); ParentTemplateIdSearch.done(); + InactiveUnremovedTmpltSearch = createSearchBuilder(); + InactiveUnremovedTmpltSearch.and("state", InactiveUnremovedTmpltSearch.entity().getState(), SearchCriteria.Op.IN); + InactiveUnremovedTmpltSearch.and("removed", InactiveUnremovedTmpltSearch.entity().getRemoved(), SearchCriteria.Op.NULL); + InactiveUnremovedTmpltSearch.done(); + return result; } @@ -938,6 +944,13 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem return customSearch(sc, null).get(0); } + @Override + public List listUnRemovedTemplatesByStates(VirtualMachineTemplate.State ...states) { + SearchCriteria sc = InactiveUnremovedTmpltSearch.create(); + sc.setParameters("state", (Object[]) states); + return listBy(sc); + } + @Override @DB public boolean remove(Long id) { diff --git a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java index 262fa4b4ff1..e344b462b50 100644 --- a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java +++ b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java @@ -1008,8 +1008,17 @@ public class CommandSetupHelper { final boolean setupDns = dnsProvided || dhcpProvided; if (setupDns) { - defaultDns1 = guestNic.getIPv4Dns1(); - defaultDns2 = guestNic.getIPv4Dns2(); + final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId()); + if (guestNic.getIPv4Dns1() != null) { + defaultDns1 = guestNic.getIPv4Dns1(); + } else { + defaultDns1 = dcVo.getDns1(); + } + if (guestNic.getIPv4Dns2() != null) { + defaultDns2 = guestNic.getIPv4Dns2(); + } else { + defaultDns2 = dcVo.getDns2(); + } } final Nic nic = _nicDao.findByNtwkIdAndInstanceId(network.getId(), router.getId()); diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index a7cc7724abc..62119ff1edd 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -1221,6 +1221,14 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C return new Pair(result.first(), result.second()[0]); } + private void cleanupInactiveTemplates() { + List vmTemplateVOS = _templateDao.listUnRemovedTemplatesByStates(VirtualMachineTemplate.State.Inactive); + for (VMTemplateVO template: vmTemplateVOS) { + template.setRemoved(new Date()); + _templateDao.update(template.getId(), template); + } + } + @Override public void cleanupStorage(boolean recurring) { GlobalLock scanLock = GlobalLock.getInternLock("storagemgr.cleanup"); @@ -1393,6 +1401,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C s_logger.warn("Unable to destroy uploaded template " + template.getUuid() + ". Error details: " + th.getMessage()); } } + cleanupInactiveTemplates(); } finally { scanLock.unlock(); } diff --git a/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java b/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java index 30fb6a141f3..aed1d87b306 100644 --- a/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java +++ b/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java @@ -18,11 +18,13 @@ package com.cloud.template; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; import javax.inject.Inject; @@ -473,6 +475,18 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { return null; } + boolean cleanupTemplate(VMTemplateVO template, boolean success) { + List templateZones = templateZoneDao.listByTemplateId(template.getId()); + List zoneIds = templateZones.stream().map(VMTemplateZoneVO::getZoneId).collect(Collectors.toList()); + if (zoneIds.size() > 0) { + return success; + } + template.setRemoved(new Date()); + template.setState(State.Inactive); + templateDao.update(template.getId(), template); + return success; + } + @Override @DB public boolean delete(TemplateProfile profile) { @@ -594,7 +608,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { if (success) { if ((imageStores != null && imageStores.size() > 1) && (profile.getZoneIdList() != null)) { //if template is stored in more than one image stores, and the zone id is not null, then don't delete other templates. - return success; + return cleanupTemplate(template, success); } // delete all cache entries for this template @@ -617,6 +631,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { // Mark template as Inactive. template.setState(VirtualMachineTemplate.State.Inactive); + _tmpltDao.remove(template.getId()); _tmpltDao.update(template.getId(), template); // Decrement the number of templates and total secondary storage diff --git a/systemvm/debian/opt/cloud/bin/setup/router.sh b/systemvm/debian/opt/cloud/bin/setup/router.sh index e1fe8cd9646..7c9549354e1 100755 --- a/systemvm/debian/opt/cloud/bin/setup/router.sh +++ b/systemvm/debian/opt/cloud/bin/setup/router.sh @@ -99,6 +99,9 @@ setup_router() { if [ -f /etc/cron.daily/logrotate ]; then mv -n /etc/cron.daily/logrotate /etc/cron.hourly 2>&1 fi + + # Load modules to support NAT traversal in VR + modprobe nf_nat_pptp } routing_svcs diff --git a/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh b/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh index 9e6cdb7b74b..68877821fb3 100755 --- a/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh +++ b/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh @@ -112,6 +112,9 @@ EOF if [ -f /etc/cron.daily/logrotate ]; then mv -n /etc/cron.daily/logrotate /etc/cron.hourly 2>&1 fi + + # Load modules to support NAT traversal in VR + modprobe nf_nat_pptp } routing_svcs diff --git a/test/integration/smoke/test_templates.py b/test/integration/smoke/test_templates.py index dd724b8171c..0947b4b62eb 100644 --- a/test/integration/smoke/test_templates.py +++ b/test/integration/smoke/test_templates.py @@ -1322,6 +1322,7 @@ class TestCreateTemplateWithDirectDownload(cloudstackTestCase): """ self.template["checksum"]="{MD5}XXXXXXX" tmpl = Template.register(self.apiclient, self.template, zoneid=self.zone.id, hypervisor=self.hypervisor, randomize_name=False) + self.cleanup.append(tmpl) try: virtual_machine = VirtualMachine.create( @@ -1332,11 +1333,9 @@ class TestCreateTemplateWithDirectDownload(cloudstackTestCase): domainid=self.account.domainid, serviceofferingid=self.service_offering.id ) - self.cleanup.append(tmpl) + self.cleanup.append(virtual_machine) self.fail("Expected to fail deployment") except Exception as e: self.debug("Expected exception") - self.cleanup.append(virtual_machine) - self.cleanup.append(tmpl) return diff --git a/ui/.env.local b/ui/.env.local new file mode 100644 index 00000000000..92ec87a7b0d --- /dev/null +++ b/ui/.env.local @@ -0,0 +1 @@ +CS_URL=http://10.0.35.240:8080 \ No newline at end of file diff --git a/ui/src/views/auth/Login.vue b/ui/src/views/auth/Login.vue index a43b61191e2..a3fdf3bb96a 100644 --- a/ui/src/views/auth/Login.vue +++ b/ui/src/views/auth/Login.vue @@ -189,8 +189,8 @@ export default { } Login(loginParams) .then((res) => this.loginSuccess(res)) - .catch(err => this.requestFailed(err)) - .finally(() => { + .catch(err => { + this.requestFailed(err) state.loginBtn = false }) } else if (customActiveKey === 'saml') { diff --git a/utils/src/main/java/org/apache/cloudstack/utils/redfish/RedfishClient.java b/utils/src/main/java/org/apache/cloudstack/utils/redfish/RedfishClient.java index 8b211c02606..32b4f383e94 100644 --- a/utils/src/main/java/org/apache/cloudstack/utils/redfish/RedfishClient.java +++ b/utils/src/main/java/org/apache/cloudstack/utils/redfish/RedfishClient.java @@ -231,8 +231,9 @@ public class RedfishClient { for (int attempt = 1; attempt < redfishRequestMaxRetries + 1; attempt++) { try { TimeUnit.SECONDS.sleep(WAIT_FOR_REQUEST_RETRY); - LOGGER.debug(String.format("Retry HTTP %s request [URL: %s], attempt %d/%d.", httpReq.getMethod(), url, attempt, redfishRequestMaxRetries)); + LOGGER.debug(String.format("HTTP %s request retry attempt %d/%d [URL: %s].", httpReq.getMethod(), url, attempt, redfishRequestMaxRetries)); response = client.execute(httpReq); + break; } catch (IOException | InterruptedException e) { if (attempt == redfishRequestMaxRetries) { throw new RedfishException(String.format("Failed to execute HTTP %s request retry attempt %d/%d [URL: %s] due to exception %s", httpReq.getMethod(), attempt, redfishRequestMaxRetries,url, e)); diff --git a/utils/src/test/java/org/apache/cloudstack/utils/redfish/RedfishClientTest.java b/utils/src/test/java/org/apache/cloudstack/utils/redfish/RedfishClientTest.java index 15a75bab212..674700bd412 100644 --- a/utils/src/test/java/org/apache/cloudstack/utils/redfish/RedfishClientTest.java +++ b/utils/src/test/java/org/apache/cloudstack/utils/redfish/RedfishClientTest.java @@ -207,4 +207,26 @@ public class RedfishClientTest { Mockito.verify(newRedfishClientspy, Mockito.times(1)).retryHttpRequest(Mockito.anyString(), Mockito.any(), Mockito.any()); Mockito.verify(client, Mockito.times(3)).execute(Mockito.any()); } + + @Test(expected = RedfishException.class) + public void retryHttpRequestExceptionAfterTwoRetries() throws IOException { + Mockito.when(client.execute(httpReq)).thenThrow(IOException.class).thenThrow(IOException.class); + + RedfishClient newRedfishClientspy = Mockito.spy(new RedfishClient(USERNAME, PASSWORD, true, true, REDFISHT_REQUEST_RETRIES)); + newRedfishClientspy.retryHttpRequest(url, httpReq, client); + + Mockito.verify(newRedfishClientspy, Mockito.never()).retryHttpRequest(Mockito.anyString(), Mockito.any(), Mockito.any()); + Mockito.verify(client, Mockito.never()).execute(Mockito.any()); + } + + @Test + public void retryHttpRequestSuccessAtTheSecondRetry() throws IOException { + Mockito.when(client.execute(httpReq)).thenThrow(IOException.class).thenReturn(httpResponse); + + RedfishClient newRedfishClientspy = Mockito.spy(new RedfishClient(USERNAME, PASSWORD, true, true, REDFISHT_REQUEST_RETRIES)); + newRedfishClientspy.retryHttpRequest(url, httpReq, client); + + Mockito.verify(newRedfishClientspy, Mockito.times(1)).retryHttpRequest(Mockito.anyString(), Mockito.any(), Mockito.any()); + Mockito.verify(client, Mockito.times(REDFISHT_REQUEST_RETRIES)).execute(Mockito.any()); + } }