From 48df255f71e9938cc9e0879cbc60d9a0a778d95a Mon Sep 17 00:00:00 2001 From: Nitin Kumar Maharana Date: Tue, 17 Nov 2015 23:02:55 +0530 Subject: [PATCH 1/3] CLOUDSTACK-9068: Listing Port Forwarding Rules take too much time to load For setting the width of each data item for each row of Port Forwarding rules, it was processing all rules. Basically for each data item, it was searching in all rules, which is un-necessary. If there are N-Rules, It was processing N-times. Now, it only processes one time by taking all N-rules at a time. The previous solution was of O(NxN). Now its changed to O(N). --- ui/scripts/ui/widgets/multiEdit.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui/scripts/ui/widgets/multiEdit.js b/ui/scripts/ui/widgets/multiEdit.js index 02d231d7f15..36ae27b3246 100755 --- a/ui/scripts/ui/widgets/multiEdit.js +++ b/ui/scripts/ui/widgets/multiEdit.js @@ -278,9 +278,6 @@ $td.addClass('blank'); } - // Align width to main header - _medit.refreshItemWidths($multi); - if (data._hideFields && $.inArray(fieldName, data._hideFields) > -1) { $td.addClass('disabled'); From 6e5a05d5c97f96afa656596016fb11df6c222dfb Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Fri, 16 Jan 2015 13:53:19 +0100 Subject: [PATCH 2/3] Add select template dropdown when reinstall VM --- ui/scripts/instances.js | 48 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index 5096297f5df..56d05a006b7 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -858,10 +858,56 @@ return null; } }, + createForm: { + title: 'label.reinstall.vm', + desc: 'message.reinstall.vm', + isWarning: true, + fields: { + template: { + label: 'label.select.a.template', + select: function(args) { + var data = { + templatefilter: 'featured' + }; + $.ajax({ + url: createURL('listTemplates'), + data: data, + async: false, + success: function(json) { + var templates = json.listtemplatesresponse.template; + var items = [{ + id: -1, + description: '' + }]; + $(templates).each(function() { + items.push({ + id: this.id, + description: this.name + }); + }); + args.response.success({ + data: items + }); + } + }); + } + } + } + }, action: function(args) { + var dataObj = { + virtualmachineid: args.context.instances[0].id + }; + if (args.data.template != -1) { + $.extend(dataObj, { + templateid: args.data.template + }); + } + $.ajax({ - url: createURL("restoreVirtualMachine&virtualmachineid=" + args.context.instances[0].id), + url: createURL("restoreVirtualMachine"), + data: dataObj, dataType: "json", async: true, success: function(json) { From 7d44e90c8df13146918c66cebdbb2573e1dec4ce Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 23 Nov 2015 17:15:57 +0000 Subject: [PATCH 3/3] Fix event UUIDS missing on event bus The fixing of CLOUDSTACK-8816 introduced a regression that removed the first class entities in the event bus description property. This is because everything was changed to use the Class as a key... Everything but the populateFirstClassEntities method in ActionEventUtils. --- .../src/com/cloud/event/ActionEventUtils.java | 3 +- .../com/cloud/event/ActionEventUtilsTest.java | 213 ++++++++++++++++++ 2 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 server/test/com/cloud/event/ActionEventUtilsTest.java diff --git a/server/src/com/cloud/event/ActionEventUtils.java b/server/src/com/cloud/event/ActionEventUtils.java index 0ca41cc3a73..b1dd8fdbaa1 100644 --- a/server/src/com/cloud/event/ActionEventUtils.java +++ b/server/src/com/cloud/event/ActionEventUtils.java @@ -293,8 +293,7 @@ public class ActionEventUtils { for(Map.Entry entry : contextMap.entrySet()){ try{ - Object key = entry.getKey(); - Class clz = Class.forName((String)key); + Class clz = (Class)entry.getKey(); if(clz != null && Identity.class.isAssignableFrom(clz)){ String uuid = getEntityUuid(clz, entry.getValue()); eventDescription.put(ReflectUtil.getEntityName(clz), uuid); diff --git a/server/test/com/cloud/event/ActionEventUtilsTest.java b/server/test/com/cloud/event/ActionEventUtilsTest.java new file mode 100644 index 00000000000..a7fcf53f61d --- /dev/null +++ b/server/test/com/cloud/event/ActionEventUtilsTest.java @@ -0,0 +1,213 @@ +package com.cloud.event; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.inject.Inject; + +import org.apache.cloudstack.framework.events.Event; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.events.EventBus; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + + +import com.cloud.configuration.Config; +import com.cloud.event.dao.EventDao; +import com.cloud.network.IpAddress; +import com.cloud.projects.dao.ProjectDao; +import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.UserDao; +import com.cloud.utils.component.ComponentContext; +import com.cloud.utils.db.EntityManager; +import com.cloud.vm.VirtualMachine; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(ComponentContext.class) +public class ActionEventUtilsTest { + //Predictable constants used throughout this test. + public static final long EVENT_ID = 1; + public static final long USER_ID = 1; + public static final long ACCOUNT_ID = 1; + + //Keep track of the static field values between tests. + //A horrid abuse of reflection required due to the strange + //static/inject pattern found in ActionEventUtils. + protected Map staticFieldValues = new HashMap<>(); + + //List of events published on the event bus. Handled via a mocked method. + //Cleared on every run. + protected List publishedEvents = new ArrayList<>(); + + //Mock fields. These are injected into ActionEventUtils by the setup() method. + @Mock + protected EventDao eventDao; + + @Mock + protected AccountDao accountDao; + + @Mock + protected UserDao userDao; + + @Mock + protected ProjectDao projectDao; + + @Mock + protected EntityManager entityMgr; + + @Mock + protected ConfigurationDao configDao; + + @Mock + protected EventBus eventBus; + + /** + * This setup method injects the mocked beans into the ActionEventUtils class. + * Because ActionEventUtils has static methods, we must also remember these fields + * and restore them later, as otherwise strange behavior can result in other unit + * tests due to the way the JVM handles static fields. + * @throws Exception + */ + @Before + public void setup() throws Exception { + publishedEvents = new ArrayList<>(); + staticFieldValues = new HashMap<>(); + setupCommonMocks(); + + ActionEventUtils utils = new ActionEventUtils(); + + for (Field field : ActionEventUtils.class.getDeclaredFields()) { + if (field.getAnnotation(Inject.class) != null) { + field.setAccessible(true); + + try { + //Inject the mocked field from this class into the ActionEventUtils + //and keep track of its original value. + Field mockField = this.getClass().getDeclaredField(field.getName()); + field.set(utils, mockField.get(this)); + Field staticField = ActionEventUtils.class.getDeclaredField("s_" + field.getName()); + staticFieldValues.put(field.getName(), staticField.get(null)); + } + catch (Exception e) { + // ignore missing fields + } + } + } + + utils.init(); + } + + /** + * Set up the common specialized mocks that are needed to make the ActionEventUtils class behave in a + * predictable way. This method only mocks things that are common to all the tests. Each individual test + * also mocks some other methods (e.g. find user/account) by itself. + */ + public void setupCommonMocks() throws Exception { + //Some basic mocks. + Mockito.when(configDao.getValue(Config.PublishActionEvent.key())).thenReturn("true"); + PowerMockito.mockStatic(ComponentContext.class); + Mockito.when(ComponentContext.getComponent(EventBus.class)).thenReturn(eventBus); + + //Needed for persist to actually set an ID that can be returned from the ActionEventUtils + //methods. + Mockito.when(eventDao.persist(Mockito.any(EventVO.class))).thenAnswer(new Answer() { + @Override + public EventVO answer(InvocationOnMock invocation) throws Throwable { + EventVO event = (EventVO)invocation.getArguments()[0]; + Field id = event.getClass().getDeclaredField("id"); + id.setAccessible(true); + id.set(event, EVENT_ID); + return event; + } + }); + + //Needed to record events published on the bus. + Mockito.doAnswer(new Answer() { + @Override public Void answer(InvocationOnMock invocation) throws Throwable { + Event event = (Event)invocation.getArguments()[0]; + publishedEvents.add(event); + return null; + } + + }).when(eventBus).publish(Mockito.any(Event.class)); + } + + /** + * This teardown method restores the ActionEventUtils static field values to their original values, + * keeping the mocked mess inside this class. + */ + @After + public void teardown() { + ActionEventUtils utils = new ActionEventUtils(); + + for (String fieldName : staticFieldValues.keySet()) { + try { + Field field = ActionEventUtils.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(utils, staticFieldValues.get(fieldName)); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + utils.init(); + } + + @Test + public void testPopulateFirstClassEntities() { + AccountVO account = new AccountVO("testaccount", 1L, "networkdomain", (short) 0, "uuid"); + account.setId(ACCOUNT_ID); + UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", + UUID.randomUUID().toString(), User.Source.UNKNOWN); + + Mockito.when(accountDao.findById(ACCOUNT_ID)).thenReturn(account); + Mockito.when(userDao.findById(USER_ID)).thenReturn(user); + + CallContext.register(user, account); + + //Inject some entity UUIDs into the call context + String instanceUuid = UUID.randomUUID().toString(); + String ipUuid = UUID.randomUUID().toString(); + CallContext.current().putContextParameter(VirtualMachine.class, instanceUuid); + CallContext.current().putContextParameter(IpAddress.class, ipUuid); + + ActionEventUtils.onActionEvent(USER_ID, ACCOUNT_ID, account.getDomainId(), "StaticNat", "Test event"); + + //Assertions + Assert.assertNotEquals(publishedEvents.size(), 0); + Assert.assertEquals(publishedEvents.size(), 1); + + Event event = publishedEvents.get(0); + Assert.assertNotNull(event.getDescription()); + + JsonObject json = new JsonParser().parse(event.getDescription()).getAsJsonObject(); + + Assert.assertTrue(json.has("VirtualMachine")); + Assert.assertTrue(json.has("IpAddress")); + Assert.assertEquals(json.get("VirtualMachine").getAsString(), instanceUuid); + Assert.assertEquals(json.get("IpAddress").getAsString(), ipUuid); + + CallContext.unregister(); + } +}