Michael Pasternak has uploaded a new change for review.

Change subject: restapi: user cannnot take a vm from pool via restapi #864438
......................................................................

restapi: user cannnot take a vm from pool via restapi #864438

This patch implements new action /allocatevm in vmpool [1] for user
to take vm from the pool,

also this patches introduces new generic and type independent resolver
pattern callback on the action, in this case BE action on vmpool returns
vm id which is later transformed to vm ref in api action response.

[1] POST /api/vmpools/{vmpool:id}/allocatevm

https://bugzilla.redhat.com/show_bug.cgi?id=864438

Change-Id: Icdd917a5f443bd7e8d06b582ae4559bef90608e6
Signed-off-by: Michael Pasternak <mpast...@redhat.com>
---
M 
backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/resource/VmPoolResource.java
M 
backend/manager/modules/restapi/interface/definition/src/main/resources/rsdl_metadata_v-3.1.yaml
M 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/AbstractBackendActionableResource.java
M 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/AbstractBackendResource.java
M 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmPoolResource.java
A 
backend/manager/modules/restapi/jaxrs/src/test/java/org/ovirt/engine/api/restapi/resource/BackendVmPoolResourceTest.java
6 files changed, 369 insertions(+), 2 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/88/8488/1

diff --git 
a/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/resource/VmPoolResource.java
 
b/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/resource/VmPoolResource.java
index 130d43a..e90ef66 100644
--- 
a/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/resource/VmPoolResource.java
+++ 
b/backend/manager/modules/restapi/interface/definition/src/main/java/org/ovirt/engine/api/resource/VmPoolResource.java
@@ -16,13 +16,31 @@
 
 package org.ovirt.engine.api.resource;
 
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.providers.jaxb.Formatted;
+import org.ovirt.engine.api.model.Action;
+import org.ovirt.engine.api.model.Actionable;
 import org.ovirt.engine.api.model.VmPool;
 
 
 @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, 
MediaType.APPLICATION_X_YAML})
-public interface VmPoolResource extends UpdatableResource<VmPool> {
+public interface VmPoolResource extends UpdatableResource<VmPool>, 
AsynchronouslyCreatedResource {
+
+    @Path("{action: (allocatevm)}/{oid}")
+    public ActionResource getActionSubresource(@PathParam("action")String 
action, @PathParam("oid")String oid);
+
+    @POST
+    @Formatted
+    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, 
MediaType.APPLICATION_X_YAML})
+    @Actionable
+    @Path("allocatevm")
+    public Response allocatevm(Action action);
 
     @Path("permissions")
     public AssignedPermissionsResource getPermissionsResource();
diff --git 
a/backend/manager/modules/restapi/interface/definition/src/main/resources/rsdl_metadata_v-3.1.yaml
 
b/backend/manager/modules/restapi/interface/definition/src/main/resources/rsdl_metadata_v-3.1.yaml
index dfc4143..a6a9fd3 100644
--- 
a/backend/manager/modules/restapi/interface/definition/src/main/resources/rsdl_metadata_v-3.1.yaml
+++ 
b/backend/manager/modules/restapi/interface/definition/src/main/resources/rsdl_metadata_v-3.1.yaml
@@ -2435,6 +2435,17 @@
     headers:
       Content-Type: {value: application/xml|json, required: true}
       Correlation-Id: {value: 'any string', required: false}
+- name: /api/vmpools/{vmpool:id}/allocatevm|rel=allocatevm
+  request:
+    body:
+      parameterType: Action
+      signatures:
+      - mandatoryArguments: {}
+        optionalArguments: {action.async: 'xs:boolean'}
+    urlparams: {}
+    headers:
+      Content-Type: {value: application/xml|json, required: true}
+      Correlation-Id: {value: 'any string', required: false}
 - name: /api/vmpools|rel=add
   request:
     body:
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/AbstractBackendActionableResource.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/AbstractBackendActionableResource.java
index 22b6cb2..7bd25a9 100644
--- 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/AbstractBackendActionableResource.java
+++ 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/AbstractBackendActionableResource.java
@@ -1,5 +1,6 @@
 package org.ovirt.engine.api.restapi.resource;
 
+import java.lang.reflect.Method;
 import java.net.URI;
 
 import javax.ws.rs.WebApplicationException;
@@ -32,6 +33,39 @@
         super(id, modelType, entityType, subCollections);
     }
 
+    protected Response doAction(final VdcActionType task, final 
VdcActionParametersBase params, final Action action, 
AbstractBackendResource.PollingType pollingType, EntityResolver entityResolver) 
{
+        awaitGrace(action);
+        try {
+            VdcReturnValueBase actionResult = doAction(task, params);
+            if (actionResult.getHasAsyncTasks()) {
+                if (expectBlocking(action)) {
+                    Object model = resolveCreated(actionResult, 
entityResolver, null);
+                    CreationStatus status = awaitCompletion(actionResult, 
pollingType);
+                    return actionStatus(status, action, model);
+                } else {
+                    return actionAsync(actionResult, action);
+                }
+            } else {
+                Object model = resolveCreated(actionResult, entityResolver, 
null);
+                return actionSuccess(action, model);
+            }
+        } catch (Exception e) {
+            return handleError(e, action);
+        }
+    }
+
+    protected Object resolveCreated(VdcReturnValueBase result, EntityResolver 
entityResolver, Class<? extends BaseResource> suggestedParentType) {
+        try {
+            return entityResolver.resolve((Guid)result.getActionReturnValue());
+        } catch (Exception e) {
+            // we tolerate a failure in the entity resolution
+            // as the substantive action (entity creation) has
+            // already succeeded
+            e.printStackTrace();
+            return null;
+        }
+    }
+
     protected Response doAction(final VdcActionType task, final 
VdcActionParametersBase params, final Action action, 
AbstractBackendResource.PollingType pollingType) {
         awaitGrace(action);
         try {
@@ -49,6 +83,21 @@
         } catch (Exception e) {
             return handleError(e, action);
         }
+    }
+
+    /**
+     * Perform an action, managing asynchrony and returning an appropriate
+     * response with entity returned by backend in action body.
+     *
+     * @param uriInfo  wraps the URI for the current request
+     * @param task     the backend task
+     * @param params   the task parameters
+     * @param action   action representation
+     * @param entityResolver   backend response resolver
+     * @return
+     */
+    protected Response doAction(final VdcActionType task, final 
VdcActionParametersBase params, final Action action, EntityResolver 
entityResolver) {
+        return doAction(task, params, action, PollingType.VDSM_TASKS, 
entityResolver);
     }
 
     /**
@@ -129,11 +178,40 @@
         return Response.ok().entity(action).build();
     }
 
+    private Response actionSuccess(Action action, Object result) {
+        setActionItem(action, result);
+        action.setStatus(StatusUtils.create(CreationStatus.COMPLETE));
+        return Response.ok().entity(action).build();
+    }
+
+    private void setActionItem(Action action, Object result) {
+        String name = result.getClass().getSimpleName().toLowerCase();
+        for (Method m : action.getClass().getMethods()) {
+            if (m.getName().startsWith("set") && m.getName().replace("set", 
"").toLowerCase().equals(name)) {
+                try {
+                    m.invoke(action, result);
+                    break;
+                } catch (Exception e) {
+                    // should not happen
+                    LOG.error("Resource to action asignment failure.", e);
+                    e.printStackTrace();
+                    break;
+                }
+            }
+        }
+    }
+
     protected Response actionStatus(CreationStatus status, Action action) {
         action.setStatus(StatusUtils.create(status));
         return Response.ok().entity(action).build();
     }
 
+    protected Response actionStatus(CreationStatus status, Action action, 
Object result) {
+        setActionItem(action, result);
+        action.setStatus(StatusUtils.create(status));
+        return Response.ok().entity(action).build();
+    }
+
     protected Response actionAsync(VdcReturnValueBase actionResult, Action 
action) {
         action.setAsync(true);
 
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/AbstractBackendResource.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/AbstractBackendResource.java
index 62457a6..b0f385e 100644
--- 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/AbstractBackendResource.java
+++ 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/AbstractBackendResource.java
@@ -373,6 +373,19 @@
         }
     }
 
+    protected abstract class EntityResolver {
+
+        public abstract Object lookupEntity(Guid id) throws 
BackendFailureException;
+
+        public Object resolve(Guid id) throws BackendFailureException {
+            Object entity = lookupEntity(id);
+            if (entity == null) {
+                throw new EntityNotFoundException(id.toString());
+            }
+            return entity;
+        }
+    }
+
     protected class QueryIdResolver extends EntityIdResolver {
 
         private VdcQueryType query;
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmPoolResource.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmPoolResource.java
index feb1d28..5e5ba5e 100644
--- 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmPoolResource.java
+++ 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendVmPoolResource.java
@@ -3,29 +3,39 @@
 
 import java.util.List;
 
+import javax.ws.rs.core.Response;
+
+import org.ovirt.engine.api.common.util.LinkHelper;
+import org.ovirt.engine.api.model.Action;
 import org.ovirt.engine.api.model.VmPool;
+import org.ovirt.engine.api.resource.ActionResource;
 import org.ovirt.engine.api.resource.AssignedPermissionsResource;
+import org.ovirt.engine.api.resource.CreationResource;
 import org.ovirt.engine.api.resource.VmPoolResource;
 import org.ovirt.engine.core.common.VdcObjectType;
 import org.ovirt.engine.core.common.action.AddVmPoolWithVmsParameters;
 import org.ovirt.engine.core.common.action.VdcActionParametersBase;
 import org.ovirt.engine.core.common.action.VdcActionType;
+import org.ovirt.engine.core.common.action.VmPoolUserParameters;
 import org.ovirt.engine.core.common.businessentities.DiskImage;
 import org.ovirt.engine.core.common.businessentities.VM;
 import org.ovirt.engine.core.common.businessentities.vm_pools;
 import org.ovirt.engine.core.common.businessentities.VmTemplate;
 import org.ovirt.engine.core.common.interfaces.SearchType;
 import org.ovirt.engine.core.common.queries.GetPermissionsForObjectParameters;
+import org.ovirt.engine.core.common.queries.GetVmByVmIdParameters;
 import org.ovirt.engine.core.common.queries.GetVmPoolByIdParameters;
 import org.ovirt.engine.core.common.queries.GetVmTemplatesDisksParameters;
 import org.ovirt.engine.core.common.queries.GetVmTemplateParameters;
+import org.ovirt.engine.core.common.queries.VdcQueryParametersBase;
 import org.ovirt.engine.core.common.queries.VdcQueryType;
+import org.ovirt.engine.core.common.users.VdcUser;
 import org.ovirt.engine.core.compat.Guid;
 
 import static 
org.ovirt.engine.api.restapi.resource.BackendVmPoolsResource.SUB_COLLECTION;
 
 public class BackendVmPoolResource
-    extends AbstractBackendSubResource<VmPool, vm_pools>
+    extends AbstractBackendActionableResource<VmPool, vm_pools>
     implements VmPoolResource {
 
     private BackendVmPoolsResource parent;
@@ -118,4 +128,47 @@
             return parameters;
         }
     }
+
+    @Override
+    public Response allocatevm(Action action) {
+        return doAction(VdcActionType.AttachUserToVmFromPoolAndRun,
+                        new VmPoolUserParameters(guid,  
getCurrent().get(VdcUser.class), false),
+                        action,
+                        new VmQueryIdResolver(VdcQueryType.GetVmByVmId,
+                                              GetVmByVmIdParameters.class));
+
+    }
+
+    protected class VmQueryIdResolver extends EntityResolver {
+
+        private VdcQueryType query;
+        private Class<? extends VdcQueryParametersBase> queryParamsClass;
+
+        public VmQueryIdResolver(VdcQueryType query, Class<? extends 
VdcQueryParametersBase> queryParamsClass) {
+            this.query = query;
+            this.queryParamsClass = queryParamsClass;
+        }
+
+        public org.ovirt.engine.api.model.VM lookupEntity(Guid id) throws 
BackendFailureException {
+            VM vm = doGetEntity(VM.class,
+                    query, getQueryParams(queryParamsClass, id), 
id.toString());
+            org.ovirt.engine.api.model.VM model = new 
org.ovirt.engine.api.model.VM();
+            model.setId(vm.getId().toString());
+            return LinkHelper.addLinks(getUriInfo(), model);
+        }
+    }
+
+    @Override
+    public CreationResource getCreationSubresource(String ids) {
+        return inject(new BackendCreationResource(ids));
+    }
+
+    @Override
+    public ActionResource getActionSubresource(String action, String ids) {
+        return inject(new BackendActionResource(action, ids));
+    }
+
+    public BackendVmPoolsResource getParent() {
+        return parent;
+    }
 }
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/test/java/org/ovirt/engine/api/restapi/resource/BackendVmPoolResourceTest.java
 
b/backend/manager/modules/restapi/jaxrs/src/test/java/org/ovirt/engine/api/restapi/resource/BackendVmPoolResourceTest.java
new file mode 100644
index 0000000..e10d2c1
--- /dev/null
+++ 
b/backend/manager/modules/restapi/jaxrs/src/test/java/org/ovirt/engine/api/restapi/resource/BackendVmPoolResourceTest.java
@@ -0,0 +1,194 @@
+
+package org.ovirt.engine.api.restapi.resource;
+
+import static org.easymock.EasyMock.expect;
+import java.util.ArrayList;
+
+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.VmPool;
+import org.ovirt.engine.core.common.action.VdcActionParametersBase;
+import org.ovirt.engine.core.common.action.VdcActionType;
+import org.ovirt.engine.core.common.action.VmPoolUserParameters;
+import org.ovirt.engine.core.common.businessentities.AsyncTaskStatus;
+import org.ovirt.engine.core.common.businessentities.VM;
+import org.ovirt.engine.core.common.businessentities.VmPoolType;
+import org.ovirt.engine.core.common.businessentities.vm_pools;
+import org.ovirt.engine.core.common.queries.GetVmByVmIdParameters;
+import org.ovirt.engine.core.common.queries.GetVmPoolByIdParameters;
+import org.ovirt.engine.core.common.queries.VdcQueryType;
+import org.ovirt.engine.core.common.users.VdcUser;
+import org.ovirt.engine.core.compat.Guid;
+
+public class BackendVmPoolResourceTest
+        extends AbstractBackendSubResourceTest<VmPool, vm_pools, 
BackendVmPoolResource> {
+
+    public BackendVmPoolResourceTest() {
+        super(new BackendVmPoolResource(GUIDS[0].toString(), new 
BackendVmPoolsResource()));
+    }
+
+    @Override
+    protected void init() {
+        super.init();
+        resource.getParent().backend = backend;
+        resource.getParent().sessionHelper = sessionHelper;
+        resource.getParent().mappingLocator = resource.mappingLocator;
+        resource.getParent().httpHeaders = httpHeaders;
+    }
+
+    @Test
+    public void testBadGuid() throws Exception {
+        control.replay();
+        try {
+            new BackendVmPoolResource("foo", new BackendVmPoolsResource());
+            fail("expected WebApplicationException");
+        } catch (WebApplicationException wae) {
+            verifyNotFoundException(wae);
+        }
+    }
+
+    @Test
+    public void testGetNotFound() throws Exception {
+        setUriInfo(setUpBasicUriExpectations());
+        setUpGetEntityExpectations(1, true);
+        control.replay();
+        try {
+            resource.get();
+            fail("expected WebApplicationException");
+        } catch (WebApplicationException wae) {
+            verifyNotFoundException(wae);
+        }
+    }
+
+    @Test
+    public void testGet() throws Exception {
+        setUriInfo(setUpBasicUriExpectations());
+        setUpGetEntityExpectations(1);
+        control.replay();
+        verifyModel(resource.get(), 0);
+    }
+
+    @Test
+    public void testAllocateVm() throws Exception {
+        setUpGetVmExpectations(1);
+        setUpGetUserExpectations();
+        
setUriInfo(setUpActionExpectations(VdcActionType.AttachUserToVmFromPoolAndRun,
+                                           VmPoolUserParameters.class,
+                                           new String[] { "VmPoolId", 
"IsInternal" },
+                                           new Object[] { GUIDS[0], 
Boolean.FALSE },
+                                           GUIDS[0]));
+
+        verifyTestAllocateVmActionResponse(resource.allocatevm(new Action()));
+    }
+
+
+    private void setUpGetUserExpectations() {
+        VdcUser user = new VdcUser();
+        user.setUserId(GUIDS[0]);
+        
expect(sessionHelper.getCurrent().get(VdcUser.class)).andReturn(user).anyTimes();
+    }
+
+    private void setUpGetVmExpectations(int times) throws Exception {
+        while (times-- > 0) {
+            setUpGetEntityExpectations(VdcQueryType.GetVmByVmId,
+                                       GetVmByVmIdParameters.class,
+                                       new String[] { "Id" },
+                                       new Object[] { GUIDS[0] },
+                                       getVmEntity());
+        }
+    }
+
+    private VM getVmEntity() {
+        return getVmEntity(0);
+    }
+
+    protected VM getVmEntity(int index) {
+        return setUpVmEntityExpectations(
+                control.createMock(VM.class),
+                index);
+    }
+
+    private VM setUpVmEntityExpectations(VM entity, int index) {
+        expect(entity.getId()).andReturn(GUIDS[index]).anyTimes();
+
+        return entity;
+    }
+
+    protected void setUpGetEntityExpectations(int times) throws Exception {
+        setUpGetEntityExpectations(times, false);
+    }
+
+    protected void setUpGetEntityExpectations(int times, boolean notFound) 
throws Exception {
+        setUpGetEntityExpectations(times, notFound, getEntity(0));
+    }
+
+    protected void setUpGetEntityExpectations(int times, boolean notFound, 
org.ovirt.engine.core.common.businessentities.vm_pools entity) throws Exception 
{
+
+        while (times-- > 0) {
+            setUpGetEntityExpectations(VdcQueryType.GetVmPoolById,
+                                       GetVmPoolByIdParameters.class,
+                                       new String[] { "PoolId" },
+                                       new Object[] { GUIDS[0] },
+                                       notFound ? null : entity);
+        }
+    }
+
+    protected UriInfo setUpActionExpectations(VdcActionType task,
+                                              Class<? extends 
VdcActionParametersBase> clz,
+                                              String[] names,
+                                              Object[] values,
+                                              Object taskReturn) {
+        return setUpActionExpectations(task, clz, names, values, true, true, 
taskReturn, null, true);
+    }
+
+    protected UriInfo setUpActionExpectations(VdcActionType task,
+                                              Class<? extends 
VdcActionParametersBase> clz,
+                                              String[] names,
+                                              Object[] values,
+                                              ArrayList<Guid> asyncTasks,
+                                              ArrayList<AsyncTaskStatus> 
asyncStatuses) {
+        String uri = "vmpools/" + GUIDS[0] + "/action";
+        return setUpActionExpectations(task, clz, names, values, true, true, 
null, asyncTasks, asyncStatuses, null, null, uri, true);
+    }
+
+    private void verifyTestAllocateVmActionResponse(Response r) throws 
Exception {
+        assertNotNull(r.getEntity());
+        
assertNotNull(((org.ovirt.engine.api.model.Action)r.getEntity()).getVm());
+        
assertNotNull(((org.ovirt.engine.api.model.Action)r.getEntity()).getVm().getId());
+        
assertEquals((((org.ovirt.engine.api.model.Action)r.getEntity()).getVm()).getId(),
 GUIDS[0].toString());
+
+        verifyActionResponse(r, "vmpools/" + GUIDS[0], false);
+    }
+
+    protected void verifyModel(VmPool model, int index) {
+        super.verifyModel(model, index);
+        verifyModelSpecific(model, index);
+    }
+
+    static void verifyModelSpecific(VmPool model, int index) {
+        assertNotNull(model.getCluster());
+        assertNotNull(model.getCluster().getId());
+    }
+
+    @Override
+    protected vm_pools getEntity(int index) {
+        return setUpEntityExpectations(
+                control.createMock(vm_pools.class),
+                index);
+    }
+
+    private vm_pools setUpEntityExpectations(vm_pools entity, int index) {
+        expect(entity.getvm_pool_id()).andReturn(GUIDS[index]).anyTimes();
+        expect(entity.getvds_group_id()).andReturn(GUIDS[2]).anyTimes();
+        expect(entity.getvm_pool_name()).andReturn(NAMES[index]).anyTimes();
+        
expect(entity.getvm_pool_type()).andReturn(VmPoolType.Automatic).anyTimes();
+        
expect(entity.getvm_pool_description()).andReturn(DESCRIPTIONS[index]).anyTimes();
+
+        return entity;
+    }
+}


--
To view, visit http://gerrit.ovirt.org/8488
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Icdd917a5f443bd7e8d06b582ae4559bef90608e6
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Michael Pasternak <mpast...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to