/* Copyright (c) 2015 Red Hat, Inc. Licensed 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.ovirt.engine.api.restapi.resource; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.junit.Test; import org.ovirt.engine.api.model.Action; import org.ovirt.engine.api.model.Disk; import org.ovirt.engine.api.model.Statistic; import org.ovirt.engine.api.model.StorageDomain; import org.ovirt.engine.core.common.VdcObjectType; import org.ovirt.engine.core.common.action.AttachDetachVmDiskParameters; import org.ovirt.engine.core.common.action.ExportRepoImageParameters; import org.ovirt.engine.core.common.action.MoveDisksParameters; import org.ovirt.engine.core.common.action.VdcActionParametersBase; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VmDiskOperationParameterBase; import org.ovirt.engine.core.common.asynctasks.EntityInfo; import org.ovirt.engine.core.common.businessentities.VmDeviceId; import org.ovirt.engine.core.common.businessentities.storage.DiskImage; import org.ovirt.engine.core.common.businessentities.storage.DiskInterface; import org.ovirt.engine.core.common.businessentities.storage.DiskStorageType; import org.ovirt.engine.core.common.businessentities.storage.DiskVmElement; import org.ovirt.engine.core.common.businessentities.storage.ImageStatus; import org.ovirt.engine.core.common.businessentities.storage.PropagateErrors; import org.ovirt.engine.core.common.businessentities.storage.VolumeFormat; import org.ovirt.engine.core.common.businessentities.storage.VolumeType; import org.ovirt.engine.core.common.queries.IdQueryParameters; import org.ovirt.engine.core.common.queries.NameQueryParameters; import org.ovirt.engine.core.common.queries.VdcQueryParametersBase; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.common.queries.VmDeviceIdQueryParameters; import org.ovirt.engine.core.compat.Guid; public class BackendVmDiskResourceTest extends AbstractBackendSubResourceTest<Disk, org.ovirt.engine.core.common.businessentities.storage.Disk, BackendVmDiskResource> { private static final Guid VM_ID = GUIDS[0]; private static final Guid DISK_ID = GUIDS[1]; public BackendVmDiskResourceTest() { super((BackendVmDiskResource)getCollection().getDiskResource(DISK_ID.toString())); } protected static BackendVmDisksResource getCollection() { return new BackendVmDisksResource(VM_ID); } @Test public void testGetNotFound() throws Exception { setUriInfo(setUpBasicUriExpectations()); setUpEntityQueryExpectations( VdcQueryType.GetDiskByDiskId, IdQueryParameters.class, new String[] { "Id" }, new Object[] { DISK_ID }, null ); try { resource.get(); fail("expected WebApplicationException"); } catch (WebApplicationException wae) { verifyNotFoundException(wae); } } @Test public void testGet() throws Exception { setUriInfo(setUpBasicUriExpectations()); setUpEntityQueryExpectations(1); Disk disk = resource.get(); verifyModelSpecific(disk, 1); verifyLinks(disk); } @Test public void testGetIncludeStatistics() throws Exception { try { accepts.add("application/xml; detail=statistics"); setUriInfo(setUpBasicUriExpectations()); setUpEntityQueryExpectations(1); Disk disk = resource.get(); assertTrue(disk.isSetStatistics()); verifyModelSpecific(disk, 1); verifyLinks(disk); } finally { accepts.clear(); } } @Test public void testUpdateNotFound() throws Exception { setUriInfo(setUpBasicUriExpectations()); setUpEntityQueryExpectations( VdcQueryType.GetDiskByDiskId, IdQueryParameters.class, new String[] { "Id" }, new Object[] { DISK_ID }, null ); try { resource.update(getUpdate()); fail("expected WebApplicationException"); } catch (WebApplicationException wae) { verifyNotFoundException(wae); } } @Test public void testUpdate() throws Exception { setUpGetEntityExpectations(2); setUpDiskVmElementExpectations(); setUriInfo( setUpActionExpectations( VdcActionType.UpdateVmDisk, VmDiskOperationParameterBase.class, new String[] { "VmId", "DiskInfo.WipeAfterDelete" }, new Object[] { VM_ID, Boolean.FALSE }, true, true ) ); Disk disk = resource.update(getUpdate()); assertNotNull(disk); } @Test public void testUpdateReadOnly() throws Exception { setUpGetEntityExpectations(2); setUpDiskVmElementExpectations(); setUriInfo( setUpActionExpectations( VdcActionType.UpdateVmDisk, VmDiskOperationParameterBase.class, new String[] { "VmId", "DiskVmElement.ReadOnly" }, new Object[] { VM_ID, Boolean.TRUE }, true, true ) ); Disk disk = resource.update(getUpdate()); assertNotNull(disk); } @Test public void testActivate() throws Exception { setUriInfo( setUpActionExpectations( VdcActionType.HotPlugDiskToVm, VmDiskOperationParameterBase.class, new String[] { "DiskVmElement" }, new Object[] { new DiskVmElement(DISK_ID, VM_ID) }, true, true ) ); Response response = resource.activate(new Action()); assertEquals(200, response.getStatus()); } @Test public void testDeactivate() throws Exception { setUriInfo( setUpActionExpectations( VdcActionType.HotUnPlugDiskFromVm, VmDiskOperationParameterBase.class, new String[] { "DiskVmElement" }, new Object[] { new DiskVmElement(DISK_ID, VM_ID) }, true, true ) ); Response response = resource.deactivate(new Action()); assertEquals(200, response.getStatus()); } @Test public void testStatisticalQuery() throws Exception { DiskImage entity = setUpStatisticalExpectations(); @SuppressWarnings("unchecked") BackendStatisticsResource<Disk, DiskImage> statisticsResource = (BackendStatisticsResource<Disk, DiskImage>) resource.getStatisticsResource(); assertNotNull(statisticsResource); verifyQuery(statisticsResource.getQuery(), entity); } @Test public void testExport() throws Exception { setUriInfo( setUpActionExpectations( VdcActionType.ExportRepoImage, ExportRepoImageParameters.class, new String[] { "ImageGroupID", "DestinationDomainId" }, new Object[] { DISK_ID, GUIDS[3]}, true, true, null, null, true ) ); Action action = new Action(); action.setStorageDomain(new StorageDomain()); action.getStorageDomain().setId(GUIDS[3].toString()); verifyActionResponse(resource.export(action)); } @Test public void testBadGuid() throws Exception { try { new BackendStorageDomainVmResource(null, "foo"); fail("expected WebApplicationException"); } catch (WebApplicationException wae) { verifyNotFoundException(wae); } } @Test public void testIncompleteExport() throws Exception { setUriInfo(setUpBasicUriExpectations()); try { resource.export(new Action()); fail("expected WebApplicationException on incomplete parameters"); } catch (WebApplicationException wae) { verifyIncompleteException(wae, "Action", "export", "storageDomain.id|name"); } } @Test public void testRemove() throws Exception { setUriInfo(setUpBasicUriExpectations()); setUpEntityQueryExpectations(1); UriInfo uriInfo = setUpActionExpectations( VdcActionType.DetachDiskFromVm, AttachDetachVmDiskParameters.class, new String[] { "VmId", "EntityInfo" }, new Object[] { VM_ID, new EntityInfo(VdcObjectType.Disk, DISK_ID) }, true, true, false ); setUriInfo(uriInfo); verifyRemove(resource.remove()); } @Test public void testRemoveCantDo() throws Exception { doTestBadRemove(false, true, CANT_DO); } @Test public void testRemoveFailed() throws Exception { doTestBadRemove(true, false, FAILURE); } protected void doTestBadRemove(boolean valid, boolean success, String detail) throws Exception { setUriInfo(setUpBasicUriExpectations()); setUpEntityQueryExpectations(1); setUriInfo( setUpActionExpectations( VdcActionType.DetachDiskFromVm, AttachDetachVmDiskParameters.class, new String[] { "VmId", "EntityInfo" }, new Object[] { VM_ID, new EntityInfo(VdcObjectType.Disk, DISK_ID) }, valid, success ) ); try { resource.remove(); fail("expected WebApplicationException"); } catch (WebApplicationException wae) { verifyFault(wae, detail); } } protected DiskImage setUpStatisticalExpectations() throws Exception { DiskImage entity = mock(DiskImage.class); when(entity.getId()).thenReturn(DISK_ID); when(entity.getReadRate()).thenReturn(10); when(entity.getWriteRate()).thenReturn(20); when(entity.getReadLatency()).thenReturn(30.0); when(entity.getWriteLatency()).thenReturn(40.0); when(entity.getFlushLatency()).thenReturn(50.0); when(entity.getDiskStorageType()).thenReturn(DiskStorageType.IMAGE); setUpGetEntityExpectations(1, entity); return entity; } protected void verifyQuery(AbstractStatisticalQuery<Disk, DiskImage> query, DiskImage entity) throws Exception { assertEquals(Disk.class, query.getParentType()); assertSame(entity, query.resolve(DISK_ID)); List<Statistic> statistics = query.getStatistics(entity); verifyStatistics( statistics, new String[] { "data.current.read", "data.current.write", "disk.read.latency", "disk.write.latency", "disk.flush.latency" }, new BigDecimal[] { asDec(10), asDec(20), asDec(30.0), asDec(40.0), asDec(50.0) } ); Statistic adopted = query.adopt(new Statistic()); assertTrue(adopted.isSetDisk()); assertEquals(DISK_ID.toString(), adopted.getDisk().getId()); assertTrue(adopted.getDisk().isSetVm()); assertEquals(VM_ID.toString(), adopted.getDisk().getVm().getId()); } protected Disk getUpdate() { Disk update = new Disk(); update.setWipeAfterDelete(false); update.setReadOnly(true); return update; } @Override protected org.ovirt.engine.core.common.businessentities.storage.Disk getEntity(int index) { return setUpEntityExpectations(mock(DiskImage.class), index); } protected void setUpEntityQueryExpectations(int times) throws Exception { while (times-- > 0) { setUpEntityQueryExpectations( VdcQueryType.GetDiskByDiskId, IdQueryParameters.class, new String[] { "Id" }, new Object[] { DISK_ID }, getEntity(1) ); } } private void setUpDiskVmElementExpectations() throws Exception { DiskVmElement dve = new DiskVmElement(DISK_ID, VM_ID); dve.setDiskInterface(DiskInterface.VirtIO); dve.setBoot(false); setUpGetEntityExpectations( VdcQueryType.GetDiskVmElementById, VmDeviceIdQueryParameters.class, new String[] { "Id"}, new Object[] { new VmDeviceId(DISK_ID, VM_ID)}, dve ); } protected void setUpGetEntityExpectations(int times) throws Exception { setUpGetEntityExpectations(times, getEntity(1)); } protected void setUpGetEntityExpectations(int times, org.ovirt.engine.core.common.businessentities.storage.Disk entity) throws Exception { while (times-- > 0) { setUpGetEntityExpectations( VdcQueryType.GetDiskByDiskId, IdQueryParameters.class, new String[] { "Id" }, new Object[] { DISK_ID }, entity ); } } @Test public void testMoveBySdId() throws Exception { setUpEntityQueryExpectations( VdcQueryType.GetDiskByDiskId, IdQueryParameters.class, new String[] { "Id" }, new Object[] { DISK_ID }, getEntity(1) ); setUriInfo( setUpActionExpectations( VdcActionType.MoveDisks, MoveDisksParameters.class, new String[] {}, new Object[] {} ) ); verifyActionResponse(resource.move(setUpMoveParams(false))); } @Test public void testMoveBySdNameWithoutFilter() throws Exception { testMoveBySdName(false); } @Test public void testMoveBySdNameWithFilter() throws Exception { testMoveBySdName(true); } protected void testMoveBySdName(boolean isFiltered) throws Exception { setUriInfo(setUpBasicUriExpectations()); if (isFiltered) { setUpFilteredQueryExpectations(); setUpEntityQueryExpectations( VdcQueryType.GetAllStorageDomains, VdcQueryParametersBase.class, new String[] {}, new Object[] {}, Collections.singletonList(getStorageDomain(2)) ); } else { setUpEntityQueryExpectations( VdcQueryType.GetStorageDomainByName, NameQueryParameters.class, new String[] { "Name" }, new Object[] { NAMES[2] }, getStorageDomainStatic(2) ); } setUpEntityQueryExpectations( VdcQueryType.GetDiskByDiskId, IdQueryParameters.class, new String[] { "Id" }, new Object[] { DISK_ID }, getEntity(1) ); setUriInfo( setUpActionExpectations( VdcActionType.MoveDisks, MoveDisksParameters.class, new String[] {}, new Object[] {} ) ); verifyActionResponse(resource.move(setUpMoveParams(true))); } protected void setUpFilteredQueryExpectations() { List<String> filterValue = new ArrayList<>(); filterValue.add("true"); reset(httpHeaders); when(httpHeaders.getRequestHeader(USER_FILTER_HEADER)).thenReturn(filterValue); } protected org.ovirt.engine.core.common.businessentities.StorageDomain getStorageDomain(int idx) { org.ovirt.engine.core.common.businessentities.StorageDomain dom = new org.ovirt.engine.core.common.businessentities.StorageDomain(); dom.setId(GUIDS[idx]); dom.setStorageName(NAMES[idx]); return dom; } protected org.ovirt.engine.core.common.businessentities.StorageDomainStatic getStorageDomainStatic(int idx) { org.ovirt.engine.core.common.businessentities.StorageDomainStatic dom = new org.ovirt.engine.core.common.businessentities.StorageDomainStatic(); dom.setId(GUIDS[idx]); dom.setStorageName(NAMES[idx]); return dom; } @Test public void testIncompleteMove() throws Exception { setUriInfo(setUpBasicUriExpectations()); try { resource.move(new Action()); fail("expected WebApplicationException on incomplete parameters"); } catch (WebApplicationException wae) { verifyIncompleteException(wae, "Action", "move", "storageDomain.id|name"); } } private void verifyActionResponse(Response r) throws Exception { verifyActionResponse(r, "vms/" + VM_ID + "/disks/" + DISK_ID, false); } private Action setUpMoveParams(boolean byName) { Action action = new Action(); StorageDomain sd = new StorageDomain(); if (byName) { sd.setName(NAMES[2]); } else { sd.setId(GUIDS[3].toString()); } action.setStorageDomain(sd); return action; } protected UriInfo setUpActionExpectations(VdcActionType task, Class<? extends VdcActionParametersBase> clz, String[] names, Object[] values) { return setUpActionExpectations(task, clz, names, values, true, true, null, null, true); } private org.ovirt.engine.core.common.businessentities.storage.Disk setUpEntityExpectations(DiskImage entity, int index) { when(entity.getId()).thenReturn(GUIDS[index]); when(entity.getVmSnapshotId()).thenReturn(GUIDS[2]); when(entity.getVolumeFormat()).thenReturn(VolumeFormat.RAW); when(entity.getImageStatus()).thenReturn(ImageStatus.OK); when(entity.getVolumeType()).thenReturn(VolumeType.Sparse); when(entity.isShareable()).thenReturn(false); when(entity.getPropagateErrors()).thenReturn(PropagateErrors.On); when(entity.getDiskStorageType()).thenReturn(DiskStorageType.IMAGE); when(entity.getImageId()).thenReturn(GUIDS[1]); ArrayList<Guid> sdIds = new ArrayList<>(); sdIds.add(Guid.Empty); when(entity.getStorageIds()).thenReturn(sdIds); return setUpStatisticalEntityExpectations(entity); } private org.ovirt.engine.core.common.businessentities.storage.Disk setUpStatisticalEntityExpectations(DiskImage entity) { when(entity.getReadRate()).thenReturn(1); when(entity.getWriteRate()).thenReturn(2); when(entity.getReadLatency()).thenReturn(3.0); when(entity.getWriteLatency()).thenReturn(4.0); when(entity.getFlushLatency()).thenReturn(5.0); return entity; } static void verifyModelSpecific(Disk model, int index) { assertEquals(GUIDS[index].toString(), model.getId()); assertTrue(model.isSparse()); assertTrue(model.isPropagateErrors()); } }