Skip to content

Commit 7aec943

Browse files
author
jeff
committed
Emit template UUID and class type over event bus when deleting templates.
The behavior is now consistent with template creation. This commit also adds a unit test for this functionality to make sure that it will always happen.
1 parent 5ccebf0 commit 7aec943

File tree

2 files changed

+286
-1
lines changed

2 files changed

+286
-1
lines changed

server/src/com/cloud/template/HypervisorTemplateAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ public boolean delete(TemplateProfile profile) {
394394
// publish zone-wide usage event
395395
Long sZoneId = ((ImageStoreEntity)imageStore).getDataCenterId();
396396
if (sZoneId != null) {
397-
UsageEventUtils.publishUsageEvent(eventType, template.getAccountId(), sZoneId, template.getId(), null, null, null);
397+
UsageEventUtils.publishUsageEvent(eventType, template.getAccountId(), sZoneId, template.getId(), null, VirtualMachineTemplate.class.getName(), template.getUuid());
398398
}
399399

400400
s_logger.info("Delete template from image store: " + imageStore.getName());
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
//
18+
19+
package com.cloud.template;
20+
21+
import java.lang.reflect.Field;
22+
import java.lang.reflect.InvocationTargetException;
23+
import java.lang.reflect.Method;
24+
import java.util.ArrayList;
25+
import java.util.Collections;
26+
import java.util.HashMap;
27+
import java.util.List;
28+
import java.util.Map;
29+
import java.util.concurrent.ExecutionException;
30+
31+
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
32+
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
33+
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
34+
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService;
35+
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService.TemplateApiResult;
36+
import org.apache.cloudstack.framework.async.AsyncCallFuture;
37+
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
38+
import org.apache.cloudstack.framework.events.Event;
39+
import org.apache.cloudstack.framework.events.EventBus;
40+
import org.apache.cloudstack.framework.events.EventBusException;
41+
import org.apache.cloudstack.framework.messagebus.MessageBus;
42+
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
43+
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
44+
import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
45+
import org.junit.Assert;
46+
import org.junit.Before;
47+
import org.junit.Test;
48+
import org.junit.runner.RunWith;
49+
import org.mockito.InjectMocks;
50+
import org.mockito.Mock;
51+
import org.mockito.Mockito;
52+
import org.mockito.MockitoAnnotations;
53+
import org.mockito.invocation.InvocationOnMock;
54+
import org.mockito.stubbing.Answer;
55+
import org.powermock.api.mockito.PowerMockito;
56+
import org.powermock.core.classloader.annotations.PrepareForTest;
57+
import org.powermock.modules.junit4.PowerMockRunner;
58+
59+
import com.cloud.dc.dao.DataCenterDao;
60+
import com.cloud.event.EventTypes;
61+
import com.cloud.event.UsageEventUtils;
62+
import com.cloud.event.UsageEventVO;
63+
import com.cloud.event.dao.UsageEventDao;
64+
import com.cloud.storage.Storage.ImageFormat;
65+
import com.cloud.storage.TemplateProfile;
66+
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
67+
import com.cloud.storage.dao.VMTemplateZoneDao;
68+
import com.cloud.storage.VMTemplateVO;
69+
import com.cloud.user.AccountVO;
70+
import com.cloud.user.ResourceLimitService;
71+
import com.cloud.user.dao.AccountDao;
72+
import com.cloud.utils.component.ComponentContext;
73+
74+
import static org.mockito.Mockito.any;
75+
import static org.mockito.Mockito.anyLong;
76+
import static org.mockito.Mockito.when;
77+
import static org.mockito.Mockito.eq;
78+
import static org.mockito.Mockito.doAnswer;
79+
import static org.mockito.Mockito.mock;
80+
81+
@RunWith(PowerMockRunner.class)
82+
@PrepareForTest(ComponentContext.class)
83+
public class HypervisorTemplateAdapterTest {
84+
@Mock
85+
EventBus _bus;
86+
List<Event> events = new ArrayList<>();
87+
88+
@Mock
89+
TemplateManager _templateMgr;
90+
91+
@Mock
92+
TemplateService _templateService;
93+
94+
@Mock
95+
TemplateDataFactory _dataFactory;
96+
97+
@Mock
98+
VMTemplateZoneDao _templateZoneDao;
99+
100+
@Mock
101+
TemplateDataStoreDao _templateStoreDao;
102+
103+
@Mock
104+
UsageEventDao _usageEventDao;
105+
106+
@Mock
107+
ResourceLimitService _resourceManager;
108+
109+
@Mock
110+
MessageBus _messageBus;
111+
112+
@Mock
113+
AccountDao _accountDao;
114+
115+
@Mock
116+
DataCenterDao _dcDao;
117+
118+
@Mock
119+
ConfigurationDao _configDao;
120+
121+
@InjectMocks
122+
HypervisorTemplateAdapter _adapter;
123+
124+
//UsageEventUtils reflection abuse helpers
125+
private Map<String, Object> oldFields = new HashMap<>();
126+
private List<UsageEventVO> usageEvents = new ArrayList<>();
127+
128+
@Before
129+
public void before() {
130+
MockitoAnnotations.initMocks(this);
131+
}
132+
133+
public UsageEventUtils setupUsageUtils() throws EventBusException {
134+
Mockito.when(_configDao.getValue(eq("publish.usage.events"))).thenReturn("true");
135+
Mockito.when(_usageEventDao.persist(Mockito.any(UsageEventVO.class))).then(new Answer<Void>() {
136+
@Override public Void answer(InvocationOnMock invocation) throws Throwable {
137+
UsageEventVO vo = (UsageEventVO)invocation.getArguments()[0];
138+
usageEvents.add(vo);
139+
return null;
140+
}
141+
});
142+
143+
Mockito.when(_usageEventDao.listAll()).thenReturn(usageEvents);
144+
145+
doAnswer(new Answer<Void>() {
146+
@Override public Void answer(InvocationOnMock invocation) throws Throwable {
147+
Event event = (Event)invocation.getArguments()[0];
148+
events.add(event);
149+
return null;
150+
}
151+
}).when(_bus).publish(any(Event.class));
152+
153+
PowerMockito.mockStatic(ComponentContext.class);
154+
when(ComponentContext.getComponent(eq(EventBus.class))).thenReturn(_bus);
155+
156+
UsageEventUtils utils = new UsageEventUtils();
157+
158+
Map<String, String> usageUtilsFields = new HashMap<String, String>();
159+
usageUtilsFields.put("usageEventDao", "_usageEventDao");
160+
usageUtilsFields.put("accountDao", "_accountDao");
161+
usageUtilsFields.put("dcDao", "_dcDao");
162+
usageUtilsFields.put("configDao", "_configDao");
163+
164+
for (String fieldName : usageUtilsFields.keySet()) {
165+
try {
166+
Field f = UsageEventUtils.class.getDeclaredField(fieldName);
167+
f.setAccessible(true);
168+
//Remember the old fields for cleanup later (see cleanupUsageUtils)
169+
Field staticField = UsageEventUtils.class.getDeclaredField("s_" + fieldName);
170+
staticField.setAccessible(true);
171+
oldFields.put(f.getName(), staticField.get(null));
172+
f.set(utils,
173+
this.getClass()
174+
.getDeclaredField(
175+
usageUtilsFields.get(fieldName))
176+
.get(this));
177+
} catch (IllegalArgumentException | IllegalAccessException
178+
| NoSuchFieldException | SecurityException e) {
179+
e.printStackTrace();
180+
}
181+
182+
}
183+
try {
184+
Method method = UsageEventUtils.class.getDeclaredMethod("init");
185+
method.setAccessible(true);
186+
method.invoke(utils);
187+
} catch (SecurityException | IllegalAccessException
188+
| IllegalArgumentException | InvocationTargetException
189+
| NoSuchMethodException e) {
190+
e.printStackTrace();
191+
}
192+
193+
return utils;
194+
}
195+
196+
public void cleanupUsageUtils() {
197+
UsageEventUtils utils = new UsageEventUtils();
198+
199+
for (String fieldName : oldFields.keySet()) {
200+
try {
201+
Field f = UsageEventUtils.class.getDeclaredField(fieldName);
202+
f.setAccessible(true);
203+
f.set(utils, oldFields.get(fieldName));
204+
} catch (IllegalArgumentException | IllegalAccessException
205+
| NoSuchFieldException | SecurityException e) {
206+
e.printStackTrace();
207+
}
208+
209+
}
210+
try {
211+
Method method = UsageEventUtils.class.getDeclaredMethod("init");
212+
method.setAccessible(true);
213+
method.invoke(utils);
214+
} catch (SecurityException | NoSuchMethodException
215+
| IllegalAccessException | IllegalArgumentException
216+
| InvocationTargetException e) {
217+
e.printStackTrace();
218+
}
219+
}
220+
221+
@Test
222+
public void testEmitDeleteEventUuid() throws InterruptedException, ExecutionException, EventBusException {
223+
//All the mocks required for this test to work.
224+
ImageStoreEntity store = mock(ImageStoreEntity.class);
225+
when(store.getId()).thenReturn(1l);
226+
when(store.getDataCenterId()).thenReturn(1l);
227+
when(store.getName()).thenReturn("Test Store");
228+
229+
TemplateDataStoreVO dataStoreVO = mock(TemplateDataStoreVO.class);
230+
when(dataStoreVO.getDownloadState()).thenReturn(Status.DOWNLOADED);
231+
232+
TemplateInfo info = mock(TemplateInfo.class);
233+
when(info.getDataStore()).thenReturn(store);
234+
235+
VMTemplateVO template = mock(VMTemplateVO.class);
236+
when(template.getId()).thenReturn(1l);
237+
when(template.getName()).thenReturn("Test Template");
238+
when(template.getFormat()).thenReturn(ImageFormat.QCOW2);
239+
when(template.getAccountId()).thenReturn(1l);
240+
when(template.getUuid()).thenReturn("Test UUID"); //TODO possibly return this from method for comparison, if things work how i want
241+
242+
TemplateProfile profile = mock(TemplateProfile.class);
243+
when(profile.getTemplate()).thenReturn(template);
244+
when(profile.getZoneId()).thenReturn(1l);
245+
246+
TemplateApiResult result = mock(TemplateApiResult.class);
247+
when(result.isSuccess()).thenReturn(true);
248+
when(result.isFailed()).thenReturn(false);
249+
250+
@SuppressWarnings("unchecked")
251+
AsyncCallFuture<TemplateApiResult> future = mock(AsyncCallFuture.class);
252+
when(future.get()).thenReturn(result);
253+
254+
AccountVO acct = mock(AccountVO.class);
255+
when(acct.getId()).thenReturn(1l);
256+
when(acct.getDomainId()).thenReturn(1l);
257+
258+
when(_templateMgr.getImageStoreByTemplate(anyLong(), anyLong())).thenReturn(Collections.singletonList((DataStore)store));
259+
when(_templateStoreDao.listByTemplateStore(anyLong(), anyLong())).thenReturn(Collections.singletonList(dataStoreVO));
260+
when(_dataFactory.getTemplate(anyLong(), any(DataStore.class))).thenReturn(info);
261+
when(_dataFactory.listTemplateOnCache(anyLong())).thenReturn(Collections.singletonList(info));
262+
when(_templateService.deleteTemplateAsync(any(TemplateInfo.class))).thenReturn(future);
263+
when(_accountDao.findById(anyLong())).thenReturn(acct);
264+
when(_accountDao.findByIdIncludingRemoved(anyLong())).thenReturn(acct);
265+
266+
//Test actually begins here.
267+
setupUsageUtils();
268+
269+
_adapter.delete(profile);
270+
Assert.assertNotNull(usageEvents);
271+
Assert.assertNotNull(events);
272+
Assert.assertEquals(1, events.size());
273+
274+
Event event = events.get(0);
275+
Assert.assertNotNull(event);
276+
Assert.assertNotNull(event.getResourceType());
277+
Assert.assertEquals(VirtualMachineTemplate.class.getName(), event.getResourceType());
278+
Assert.assertNotNull(event.getResourceUUID());
279+
Assert.assertEquals("Test UUID", event.getResourceUUID());
280+
Assert.assertEquals(EventTypes.EVENT_TEMPLATE_DELETE, event.getEventType());
281+
282+
283+
cleanupUsageUtils();
284+
}
285+
}

0 commit comments

Comments
 (0)