You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by wi...@apache.org on 2013/04/03 12:33:46 UTC
[38/50] [abbrv] git commit: updated refs/heads/kvm-vnc-listen to
3890860
marvin+apidiscovery: Extend API discovery plugin
API discovery plugin will return embedded entities for marvin to
discovery and generate it's API classes.
Signed-off-by: Prasanna Santhanam <ts...@apache.org>
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/5d67c98e
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/5d67c98e
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/5d67c98e
Branch: refs/heads/kvm-vnc-listen
Commit: 5d67c98e5b7b763fde5ea87850e9cd7bafd2a4e1
Parents: d4dc264
Author: Prasanna Santhanam <ts...@apache.org>
Authored: Tue Mar 26 18:29:23 2013 +0530
Committer: Prasanna Santhanam <ts...@apache.org>
Committed: Tue Apr 2 18:31:30 2013 +0530
----------------------------------------------------------------------
client/pom.xml | 5 -
.../api/response/ApiDiscoveryResponse.java | 4 +
.../api/response/ApiResponseResponse.java | 15 ++-
.../discovery/ApiDiscoveryServiceImpl.java | 167 +++++++++------
.../cloud/api/response/ApiResponseSerializer.java | 32 +--
.../api/response/EmptyFieldExclusionStrategy.java | 40 ++++
test/integration/smoke/test_vm_life_cycle.py | 17 ++-
tools/marvin/marvin/cloudstackTestClient.py | 15 +-
tools/marvin/marvin/codegenerator.py | 96 ++++++++-
tools/marvin/marvin/marvinPlugin.py | 14 ++
10 files changed, 295 insertions(+), 110 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5d67c98e/client/pom.xml
----------------------------------------------------------------------
diff --git a/client/pom.xml b/client/pom.xml
index 91dfece..35588aa 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -91,11 +91,6 @@
<version>${project.version}</version>
</dependency>
<dependency>
- <groupId>org.apache.cloudstack</groupId>
- <artifactId>cloud-plugin-hypervisor-ucs</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-hypervisor-ovm</artifactId>
<version>${project.version}</version>
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5d67c98e/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiDiscoveryResponse.java
----------------------------------------------------------------------
diff --git a/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiDiscoveryResponse.java b/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiDiscoveryResponse.java
index 77484f0..ce7eb49 100644
--- a/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiDiscoveryResponse.java
+++ b/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiDiscoveryResponse.java
@@ -47,6 +47,9 @@ public class ApiDiscoveryResponse extends BaseResponse {
@SerializedName(ApiConstants.RESPONSE) @Param(description="api response fields", responseObject = ApiResponseResponse.class)
private Set<ApiResponseResponse> apiResponse;
+ @SerializedName(ApiConstants.TYPE) @Param(description="response field type")
+ private String type;
+
public ApiDiscoveryResponse(){
params = new HashSet<ApiParameterResponse>();
apiResponse = new HashSet<ApiResponseResponse>();
@@ -81,6 +84,7 @@ public class ApiDiscoveryResponse extends BaseResponse {
this.isAsync = isAsync;
}
+
public boolean getAsync() {
return isAsync;
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5d67c98e/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiResponseResponse.java
----------------------------------------------------------------------
diff --git a/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiResponseResponse.java b/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiResponseResponse.java
index b96295e..1433879 100644
--- a/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiResponseResponse.java
+++ b/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiResponseResponse.java
@@ -16,11 +16,14 @@
// under the License.
package org.apache.cloudstack.api.response;
-import org.apache.cloudstack.api.ApiConstants;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
+import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
+import java.util.HashSet;
+import java.util.Set;
+
public class ApiResponseResponse extends BaseResponse {
@SerializedName(ApiConstants.NAME) @Param(description="the name of the api response field")
private String name;
@@ -31,6 +34,9 @@ public class ApiResponseResponse extends BaseResponse {
@SerializedName(ApiConstants.TYPE) @Param(description="response field type")
private String type;
+ @SerializedName(ApiConstants.RESPONSE) @Param(description="api response fields")
+ private Set<ApiResponseResponse> apiResponse;
+
public void setName(String name) {
this.name = name;
}
@@ -42,4 +48,11 @@ public class ApiResponseResponse extends BaseResponse {
public void setType(String type) {
this.type = type;
}
+
+ public void addApiResponse(ApiResponseResponse childApiResponse) {
+ if(this.apiResponse == null) {
+ this.apiResponse = new HashSet<ApiResponseResponse>();
+ }
+ this.apiResponse.add(childApiResponse);
+ }
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5d67c98e/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java
----------------------------------------------------------------------
diff --git a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java
index b371488..2d7dbd1 100755
--- a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java
+++ b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java
@@ -16,25 +16,14 @@
// under the License.
package org.apache.cloudstack.discovery;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.annotation.PostConstruct;
-import javax.ejb.Local;
-import javax.inject.Inject;
-
+import com.cloud.serializer.Param;
+import com.cloud.user.User;
+import com.cloud.utils.ReflectUtil;
+import com.cloud.utils.StringUtils;
+import com.cloud.utils.component.PluggableService;
+import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.acl.APIChecker;
-import org.apache.cloudstack.api.APICommand;
-import org.apache.cloudstack.api.BaseAsyncCmd;
-import org.apache.cloudstack.api.BaseAsyncCreateCmd;
-import org.apache.cloudstack.api.BaseCmd;
-import org.apache.cloudstack.api.BaseResponse;
-import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.*;
import org.apache.cloudstack.api.command.user.discovery.ListApisCmd;
import org.apache.cloudstack.api.response.ApiDiscoveryResponse;
import org.apache.cloudstack.api.response.ApiParameterResponse;
@@ -43,12 +32,11 @@ import org.apache.cloudstack.api.response.ListResponse;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
-import com.cloud.serializer.Param;
-import com.cloud.user.User;
-import com.cloud.utils.ReflectUtil;
-import com.cloud.utils.StringUtils;
-import com.cloud.utils.component.PluggableService;
-import com.google.gson.annotations.SerializedName;
+import javax.annotation.PostConstruct;
+import javax.ejb.Local;
+import javax.inject.Inject;
+import java.lang.reflect.Field;
+import java.util.*;
@Component
@Local(value = ApiDiscoveryService.class)
@@ -69,9 +57,9 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService {
long startTime = System.nanoTime();
s_apiNameDiscoveryResponseMap = new HashMap<String, ApiDiscoveryResponse>();
Set<Class<?>> cmdClasses = new HashSet<Class<?>>();
- for(PluggableService service: _services) {
+ for(PluggableService service: _services) {
s_logger.debug(String.format("getting api commands of service: %s", service.getClass().getName()));
- cmdClasses.addAll(service.getCommands());
+ cmdClasses.addAll(service.getCommands());
}
cmdClasses.addAll(this.getCommands());
cacheResponseMap(cmdClasses);
@@ -80,72 +68,39 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService {
}
}
- protected void cacheResponseMap(Set<Class<?>> cmdClasses) {
+ protected Map<String, List<String>> cacheResponseMap(Set<Class<?>> cmdClasses) {
Map<String, List<String>> responseApiNameListMap = new HashMap<String, List<String>>();
for(Class<?> cmdClass: cmdClasses) {
APICommand apiCmdAnnotation = cmdClass.getAnnotation(APICommand.class);
- if (apiCmdAnnotation == null)
+ if (apiCmdAnnotation == null) {
apiCmdAnnotation = cmdClass.getSuperclass().getAnnotation(APICommand.class);
+ }
if (apiCmdAnnotation == null
|| !apiCmdAnnotation.includeInApiDoc()
- || apiCmdAnnotation.name().isEmpty())
+ || apiCmdAnnotation.name().isEmpty()) {
continue;
+ }
String apiName = apiCmdAnnotation.name();
+ ApiDiscoveryResponse response = getCmdRequestMap(cmdClass, apiCmdAnnotation);
+
String responseName = apiCmdAnnotation.responseObject().getName();
if (!responseName.contains("SuccessResponse")) {
- if (!responseApiNameListMap.containsKey(responseName))
+ if (!responseApiNameListMap.containsKey(responseName)) {
responseApiNameListMap.put(responseName, new ArrayList<String>());
+ }
responseApiNameListMap.get(responseName).add(apiName);
}
- ApiDiscoveryResponse response = new ApiDiscoveryResponse();
- response.setName(apiName);
- response.setDescription(apiCmdAnnotation.description());
- if (!apiCmdAnnotation.since().isEmpty())
- response.setSince(apiCmdAnnotation.since());
response.setRelated(responseName);
+
Field[] responseFields = apiCmdAnnotation.responseObject().getDeclaredFields();
for(Field responseField: responseFields) {
- SerializedName serializedName = responseField.getAnnotation(SerializedName.class);
- if(serializedName != null) {
- ApiResponseResponse responseResponse = new ApiResponseResponse();
- responseResponse.setName(serializedName.value());
- Param param = responseField.getAnnotation(Param.class);
- if (param != null)
- responseResponse.setDescription(param.description());
- responseResponse.setType(responseField.getType().getSimpleName().toLowerCase());
- response.addApiResponse(responseResponse);
- }
+ ApiResponseResponse responseResponse = getFieldResponseMap(responseField);
+ response.addApiResponse(responseResponse);
}
- Set<Field> fields = ReflectUtil.getAllFieldsForClass(cmdClass,
- new Class<?>[]{BaseCmd.class, BaseAsyncCmd.class, BaseAsyncCreateCmd.class});
-
- boolean isAsync = ReflectUtil.isCmdClassAsync(cmdClass,
- new Class<?>[] {BaseAsyncCmd.class, BaseAsyncCreateCmd.class});
-
- response.setAsync(isAsync);
-
- for(Field field: fields) {
- Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
- if (parameterAnnotation != null
- && parameterAnnotation.expose()
- && parameterAnnotation.includeInApiDoc()) {
-
- ApiParameterResponse paramResponse = new ApiParameterResponse();
- paramResponse.setName(parameterAnnotation.name());
- paramResponse.setDescription(parameterAnnotation.description());
- paramResponse.setType(parameterAnnotation.type().toString().toLowerCase());
- paramResponse.setLength(parameterAnnotation.length());
- paramResponse.setRequired(parameterAnnotation.required());
- if (!parameterAnnotation.since().isEmpty())
- paramResponse.setSince(parameterAnnotation.since());
- paramResponse.setRelated(parameterAnnotation.entityType()[0].getName());
- response.addParam(paramResponse);
- }
- }
response.setObjectName("api");
s_apiNameDiscoveryResponseMap.put(apiName, response);
}
@@ -173,6 +128,76 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService {
}
s_apiNameDiscoveryResponseMap.put(apiName, response);
}
+ return responseApiNameListMap;
+ }
+
+ private ApiResponseResponse getFieldResponseMap(Field responseField) {
+ ApiResponseResponse responseResponse = new ApiResponseResponse();
+ SerializedName serializedName = responseField.getAnnotation(SerializedName.class);
+ Param param = responseField.getAnnotation(Param.class);
+ if (serializedName != null && param != null) {
+ responseResponse.setName(serializedName.value());
+ responseResponse.setDescription(param.description());
+ responseResponse.setType(responseField.getType().getSimpleName().toLowerCase());
+ //If response is not of primitive type - we have a nested entity
+ Class fieldClass = param.responseObject();
+ if (fieldClass != null) {
+ Class<?> superClass = fieldClass.getSuperclass();
+ if (superClass != null) {
+ String superName = superClass.getName();
+ if (superName.equals(BaseResponse.class.getName())) {
+ Field[] fields = fieldClass.getDeclaredFields();
+ for (Field field : fields) {
+ ApiResponseResponse innerResponse = getFieldResponseMap(field);
+ if (innerResponse != null) {
+ responseResponse.addApiResponse(innerResponse);
+ }
+ }
+ }
+ }
+ }
+ }
+ return responseResponse;
+ }
+
+ private ApiDiscoveryResponse getCmdRequestMap(Class<?> cmdClass, APICommand apiCmdAnnotation) {
+ String apiName = apiCmdAnnotation.name();
+ ApiDiscoveryResponse response = new ApiDiscoveryResponse();
+ response.setName(apiName);
+ response.setDescription(apiCmdAnnotation.description());
+ if (!apiCmdAnnotation.since().isEmpty()) {
+ response.setSince(apiCmdAnnotation.since());
+ }
+
+
+ Set<Field> fields = ReflectUtil.getAllFieldsForClass(cmdClass,
+ new Class<?>[]{BaseCmd.class, BaseAsyncCmd.class, BaseAsyncCreateCmd.class});
+
+ boolean isAsync = ReflectUtil.isCmdClassAsync(cmdClass,
+ new Class<?>[]{BaseAsyncCmd.class, BaseAsyncCreateCmd.class});
+
+ response.setAsync(isAsync);
+
+ for(Field field: fields) {
+ Parameter parameterAnnotation = field.getAnnotation(Parameter.class);
+ if (parameterAnnotation != null
+ && parameterAnnotation.expose()
+ && parameterAnnotation.includeInApiDoc()) {
+
+ ApiParameterResponse paramResponse = new ApiParameterResponse();
+ paramResponse.setName(parameterAnnotation.name());
+ paramResponse.setDescription(parameterAnnotation.description());
+ paramResponse.setType(parameterAnnotation.type().toString().toLowerCase());
+ paramResponse.setLength(parameterAnnotation.length());
+ paramResponse.setRequired(parameterAnnotation.required());
+ if (!parameterAnnotation.since().isEmpty()) {
+ paramResponse.setSince(parameterAnnotation.since());
+ }
+ paramResponse.setRelated(parameterAnnotation.entityType()[0].getName());
+ response.addParam(paramResponse);
+ }
+ }
+ return response;
}
@Override
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5d67c98e/server/src/com/cloud/api/response/ApiResponseSerializer.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/response/ApiResponseSerializer.java b/server/src/com/cloud/api/response/ApiResponseSerializer.java
index 3b1d9a6..965660a 100644
--- a/server/src/com/cloud/api/response/ApiResponseSerializer.java
+++ b/server/src/com/cloud/api/response/ApiResponseSerializer.java
@@ -16,33 +16,25 @@
// under the License.
package com.cloud.api.response;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.cloudstack.api.response.ListResponse;
-import org.apache.cloudstack.api.response.*;
-import org.apache.log4j.Logger;
-
-import org.apache.cloudstack.api.ApiConstants;
import com.cloud.api.ApiDBUtils;
import com.cloud.api.ApiResponseGsonHelper;
import com.cloud.api.ApiServer;
-import org.apache.cloudstack.api.BaseCmd;
-import org.apache.cloudstack.api.ResponseObject;
import com.cloud.utils.encoding.URLEncoder;
import com.cloud.utils.exception.CloudRuntimeException;
-import com.cloud.uuididentity.dao.IdentityDao;
-import com.cloud.uuididentity.dao.IdentityDaoImpl;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.ResponseObject;
+import org.apache.cloudstack.api.response.*;
+import org.apache.log4j.Logger;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class ApiResponseSerializer {
private static final Logger s_logger = Logger.getLogger(ApiResponseSerializer.class.getName());
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5d67c98e/server/src/com/cloud/api/response/EmptyFieldExclusionStrategy.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/response/EmptyFieldExclusionStrategy.java b/server/src/com/cloud/api/response/EmptyFieldExclusionStrategy.java
new file mode 100644
index 0000000..3099d83
--- /dev/null
+++ b/server/src/com/cloud/api/response/EmptyFieldExclusionStrategy.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 com.cloud.api.response;
+
+import com.cloud.serializer.Param;
+import com.google.gson.ExclusionStrategy;
+import com.google.gson.FieldAttributes;
+
+public class EmptyFieldExclusionStrategy implements ExclusionStrategy {
+
+ @Override
+ public boolean shouldSkipField(FieldAttributes fieldAttributes) {
+ if (fieldAttributes.getAnnotation(Param.class) != null) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean shouldSkipClass(Class<?> aClass) {
+ return false;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5d67c98e/test/integration/smoke/test_vm_life_cycle.py
----------------------------------------------------------------------
diff --git a/test/integration/smoke/test_vm_life_cycle.py b/test/integration/smoke/test_vm_life_cycle.py
index d9571f6..564f6e8 100644
--- a/test/integration/smoke/test_vm_life_cycle.py
+++ b/test/integration/smoke/test_vm_life_cycle.py
@@ -262,11 +262,26 @@ class TestDeployVM(cloudstackTestCase):
self.assertIsNotNone(router.publicip, msg="Router has no public ip")
self.assertIsNotNone(router.guestipaddress, msg="Router has no guest ip")
+ @attr(hypervisor = ["simulator"])
+ @attr(mode = ["basic"])
+ def test_basicZoneVirtualRouter(self):
+ """
+ Tests for basic zone virtual router
+ 1. Is Running
+ 2. is in the account the VM was deployed in
+ @return:
+ """
+ routers = list_routers(self.apiclient, account=self.account.account.name)
+ self.assertTrue(len(routers) > 0, msg = "No virtual router found")
+ router = routers[0]
+
+ self.assertEqual(router.state, 'Running', msg="Router is not in running state")
+ self.assertEqual(router.account, self.account.account.name, msg="Router does not belong to the account")
+
def tearDown(self):
pass
-
class TestVMLifeCycle(cloudstackTestCase):
@classmethod
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5d67c98e/tools/marvin/marvin/cloudstackTestClient.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/cloudstackTestClient.py b/tools/marvin/marvin/cloudstackTestClient.py
index cb63179..4bfb90b 100644
--- a/tools/marvin/marvin/cloudstackTestClient.py
+++ b/tools/marvin/marvin/cloudstackTestClient.py
@@ -24,7 +24,8 @@ import string
import hashlib
class cloudstackTestClient(object):
- def __init__(self, mgtSvr=None, port=8096, apiKey = None, securityKey = None, asyncTimeout=3600, defaultWorkerThreads=10, logging=None):
+ def __init__(self, mgtSvr=None, port=8096, apiKey = None, securityKey = None, asyncTimeout=3600,
+ defaultWorkerThreads=10, logging=None):
self.connection = cloudstackConnection.cloudConnection(mgtSvr, port, apiKey, securityKey, asyncTimeout, logging)
self.apiClient = cloudstackAPIClient.CloudStackAPIClient(self.connection)
self.dbConnection = None
@@ -32,7 +33,6 @@ class cloudstackTestClient(object):
self.ssh = None
self.defaultWorkerThreads = defaultWorkerThreads
-
def dbConfigure(self, host="localhost", port=3306, user='cloud', passwd='cloud', db='cloud'):
self.dbConnection = dbConnection.dbConnection(host, port, user, passwd, db)
@@ -147,7 +147,16 @@ class cloudstackTestClient(object):
if hasattr(self, "userApiClient"):
return self.userApiClient
return None
-
+
+ def synchronize(self):
+ """
+ synchronize the api from an endpoint
+ """
+ apiclient = self.getApiClient()
+ cmd = listApis.listApisCmd()
+ response = apiclient.listApis(cmd)
+
+
'''FixME, httplib has issue if more than one thread submitted'''
def submitCmdsAndWait(self, cmds, workers=1):
if self.asyncJobMgr is None:
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5d67c98e/tools/marvin/marvin/codegenerator.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/codegenerator.py b/tools/marvin/marvin/codegenerator.py
index ed9248c..5d9a2df 100644
--- a/tools/marvin/marvin/codegenerator.py
+++ b/tools/marvin/marvin/codegenerator.py
@@ -16,10 +16,12 @@
# under the License.
import xml.dom.minidom
+import json
from optparse import OptionParser
from textwrap import dedent
import os
import sys
+
class cmdParameterProperty(object):
def __init__(self):
self.name = None
@@ -97,6 +99,7 @@ class codeGenerator:
subclass += self.space + self.space + 'self.%s = None\n'%pro.name
self.subclass.append(subclass)
+
def generate(self, cmd):
self.cmd = cmd
@@ -159,8 +162,7 @@ class codeGenerator:
fp.close()
self.code = ""
self.subclass = []
-
-
+
def finalize(self):
'''generate an api call'''
@@ -215,8 +217,7 @@ class codeGenerator:
fp.write(basecmd)
fp.close()
-
- def constructResponse(self, response):
+ def constructResponseFromXML(self, response):
paramProperty = cmdParameterProperty()
paramProperty.name = getText(response.getElementsByTagName('name'))
paramProperty.desc = getText(response.getElementsByTagName('description'))
@@ -224,7 +225,23 @@ class codeGenerator:
'''This is a list'''
paramProperty.name = paramProperty.name.split('(*)')[0]
for subresponse in response.getElementsByTagName('arguments')[0].getElementsByTagName('arg'):
- subProperty = self.constructResponse(subresponse)
+ subProperty = self.constructResponseFromXML(subresponse)
+ paramProperty.subProperties.append(subProperty)
+ return paramProperty
+
+ def constructResponseFromJSON(self, response):
+ paramProperty = cmdParameterProperty()
+ if response.has_key('name'):
+ paramProperty.name = response['name']
+ assert paramProperty.name
+
+ if response.has_key('description'):
+ paramProperty.desc = response['description']
+ if response.has_key('type') and response['type'] == 'list':
+ #Here list becomes a subproperty
+ paramProperty.name = paramProperty.name.split('(*)')[0]
+ for subresponse in response.getElementsByTagName('arguments')[0].getElementsByTagName('arg'):
+ subProperty = self.constructResponseFromXML(subresponse)
paramProperty.subProperties.append(subProperty)
return paramProperty
@@ -269,18 +286,79 @@ class codeGenerator:
if response.parentNode != responseEle:
continue
- paramProperty = self.constructResponse(response)
+ paramProperty = self.constructResponseFromXML(response)
csCmd.response.append(paramProperty)
cmds.append(csCmd)
return cmds
-
- def generateCode(self):
+
+ def loadCmdFromJSON(self, apiStream):
+ if apiStream is None:
+ raise Exception("No APIs found through discovery")
+
+ apiDict = json.loads(apiStream)
+ if not apiDict.has_key('listapisresponse'):
+ raise Exception("API discovery plugin response failed")
+ if not apiDict['listapisresponse'].has_key('count'):
+ raise Exception("Malformed api response")
+
+ apilist = apiDict['listapisresponse']['api']
+ cmds = []
+ for cmd in apilist:
+ csCmd = cloudStackCmd()
+ if cmd.has_key('name'):
+ csCmd.name = cmd['name']
+ assert csCmd.name
+
+ if cmd.has_key('description'):
+ csCmd.desc = cmd['description']
+
+ if cmd.has_key('async'):
+ csCmd.async = cmd['isasync']
+
+ for param in cmd['params']:
+ paramProperty = cmdParameterProperty()
+
+ if param.has_key('name'):
+ paramProperty.name = param['name']
+ assert paramProperty.name
+
+ if param.has_key('required'):
+ paramProperty.required = param.getElementsByTagName('required')
+
+ if param.has_key('description'):
+ paramProperty.desc = param['description']
+
+ if param.has_key('type'):
+ paramProperty.type = param['type']
+
+ csCmd.request.append(paramProperty)
+
+ for response in cmd['response']:
+ paramProperty = self.constructResponseFromJSON(response)
+ csCmd.response.append(paramProperty)
+
+ cmds.append(csCmd)
+ return cmds
+
+
+ def generateCodeFromXML(self):
cmds = self.loadCmdFromXML()
for cmd in cmds:
self.generate(cmd)
self.finalize()
+ def generateCodeFromJSON(self, apiJson):
+ """
+ Api Discovery plugin returns the supported APIs of a CloudStack endpoint.
+ @return: The classes in cloudstackAPI/ formed from api discovery json
+ """
+ with open(apiJson, 'r') as apiStream:
+ cmds = self.loadCmdFromJSON(apiStream)
+ for cmd in cmds:
+ self.generate(cmd)
+ self.finalize()
+
def getText(elements):
return elements[0].childNodes[0].nodeValue.strip()
@@ -315,5 +393,5 @@ if __name__ == "__main__":
exit(2)
cg = codeGenerator(folder, apiSpecFile)
- cg.generateCode()
+ cg.generateCodeFromXML()
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5d67c98e/tools/marvin/marvin/marvinPlugin.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/marvinPlugin.py b/tools/marvin/marvin/marvinPlugin.py
index c52596e..518f27f 100644
--- a/tools/marvin/marvin/marvinPlugin.py
+++ b/tools/marvin/marvin/marvinPlugin.py
@@ -21,6 +21,7 @@ import logging
import nose.core
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin import deployDataCenter
+from marvin import apiSynchronizer
from nose.plugins.base import Plugin
from functools import partial
@@ -39,6 +40,10 @@ class MarvinPlugin(Plugin):
self.enableOpt = "--with-marvin"
self.logformat = logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s")
+ if options.sync:
+ self.do_sync(options.config)
+ return
+
if options.debug_log:
self.logger = logging.getLogger("NoseTestExecuteEngine")
self.debug_stream = logging.FileHandler(options.debug_log)
@@ -65,6 +70,13 @@ class MarvinPlugin(Plugin):
cfg.debugLog = self.debug_stream
self.testrunner = nose.core.TextTestRunner(stream=self.result_stream, descriptions=True, verbosity=2, config=config)
+
+ def do_sync(self, config):
+ """
+ Use the ApiDiscovery plugin exposed by the CloudStack mgmt server to rebuild the cloudStack API
+ """
+ apiSynchronizer.sync(config)
+
def options(self, parser, env):
"""
@@ -84,6 +96,8 @@ class MarvinPlugin(Plugin):
help="The path to the testcase debug logs [DEBUG_LOG]")
parser.add_option("--load", action="store_true", default=False, dest="load",
help="Only load the deployment configuration given")
+ parser.add_option("--sync", action="store_true", default=False, dest="sync",
+ help="Sync the APIs from the CloudStack endpoint in marvin-config")
Plugin.options(self, parser, env)