You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ah...@apache.org on 2013/01/09 14:26:48 UTC

[26/50] [abbrv] git commit: ApiDiscovery: New plugin to help discover apis on mgmt server

ApiDiscovery: New plugin to help discover apis on mgmt server

- Introduces api/discovery plugin that helps discover apis on the mgmt server
- It's a pluggable service, therefore has it's own api-discovery_commands.properties
  where the discovery api, listApi can be blacklisted (by removing it), or it's
  role mask can be changed
- By default its response has all the apis
- Changes in other parts of the code to make it work, viz. components.xml, pom.xml,
  and in ApiServer where it is used as an adapter to get apiname, cmd mappings

The ApiDiscoveryService interface is a contract that the implementing class will
provide:

1. A means to get all the apis as a list of response, plugin is free to implement
   the response class, as long as it extends on the BaseResponse:

    ListResponse<? extends BaseResponse> listApis();

2. Provides a map of apiname as the key and cmd class as the value:

    Map<String, Class<?>> getApiNameCmdClassMapping();

Signed-off-by: Rohit Yadav <bh...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/commit/2c04dad5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/tree/2c04dad5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/diff/2c04dad5

Branch: refs/heads/javelin
Commit: 2c04dad529ff9b67f78d86316a9e1f6e93d503a4
Parents: 0482734
Author: Rohit Yadav <bh...@apache.org>
Authored: Tue Jan 8 18:48:18 2013 -0800
Committer: Rohit Yadav <bh...@apache.org>
Committed: Tue Jan 8 18:54:29 2013 -0800

----------------------------------------------------------------------
 .../org/apache/cloudstack/api/ApiConstants.java    |    5 +
 .../cloudstack/discovery/ApiDiscoveryService.java  |   29 +++
 client/pom.xml                                     |    5 +
 .../api-discovery_commands.properties.in           |   23 +++
 client/tomcatconf/components.xml.in                |    4 +
 plugins/api/discovery/pom.xml                      |   44 +++++
 .../api/command/user/discovery/ListApisCmd.java    |   55 ++++++
 .../api/response/ApiDiscoveryResponse.java         |   75 ++++++++
 .../api/response/ApiParameterResponse.java         |   70 +++++++
 .../discovery/ApiDiscoveryServiceImpl.java         |  144 +++++++++++++++
 plugins/pom.xml                                    |    1 +
 server/src/com/cloud/api/ApiServer.java            |   18 +-
 12 files changed, 462 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2c04dad5/api/src/org/apache/cloudstack/api/ApiConstants.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java
index 6d8400a..b4ce24c 100644
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -91,6 +91,7 @@ public class ApiConstants {
     public static final String INTERVAL_TYPE = "intervaltype";
     public static final String IP_ADDRESS = "ipaddress";
     public static final String IP_ADDRESS_ID = "ipaddressid";
+    public static final String IS_ASYNC = "isasync";
     public static final String IP_AVAILABLE = "ipavailable";
     public static final String IP_LIMIT = "iplimit";
     public static final String IP_TOTAL = "iptotal";
@@ -106,6 +107,7 @@ public class ApiConstants {
     public static final String JOB_STATUS = "jobstatus";
     public static final String LASTNAME = "lastname";
     public static final String LEVEL = "level";
+    public static final String LENGTH = "length";
     public static final String LIMIT_CPU_USE = "limitcpuuse";
     public static final String LOCK = "lock";
     public static final String LUN = "lun";
@@ -126,6 +128,7 @@ public class ApiConstants {
     public static final String OP = "op";
     public static final String OS_CATEGORY_ID = "oscategoryid";
     public static final String OS_TYPE_ID = "ostypeid";
+    public static final String PARAMS = "params";
     public static final String PARENT_DOMAIN_ID = "parentdomainid";
     public static final String PASSWORD = "password";
     public static final String NEW_PASSWORD = "new_password";
@@ -159,6 +162,7 @@ public class ApiConstants {
     public static final String SCHEDULE = "schedule";
     public static final String SCOPE = "scope";
     public static final String SECRET_KEY = "usersecretkey";
+    public static final String SINCE = "since";
     public static final String KEY = "key";
     public static final String SEARCH_BASE = "searchbase";
     public static final String SECURITY_GROUP_IDS = "securitygroupids";
@@ -324,6 +328,7 @@ public class ApiConstants {
     public static final String SOURCE_NAT_SUPPORTED = "sourcenatsupported";
     public static final String RESOURCE_STATE = "resourcestate";
     public static final String PROJECT_INVITE_REQUIRED = "projectinviterequired";
+    public static final String REQUIRED = "required";
     public static final String RESTART_REQUIRED = "restartrequired";
     public static final String ALLOW_USER_CREATE_PROJECTS = "allowusercreateprojects";
     public static final String CONSERVE_MODE = "conservemode";

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2c04dad5/api/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java b/api/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java
new file mode 100644
index 0000000..1220694
--- /dev/null
+++ b/api/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java
@@ -0,0 +1,29 @@
+// 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 org.apache.cloudstack.discovery;
+
+import com.cloud.utils.component.Adapter;
+import com.cloud.utils.component.PluggableService;
+import org.apache.cloudstack.api.BaseResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+
+import java.util.Map;
+
+public interface ApiDiscoveryService extends Adapter, PluggableService {
+    ListResponse<? extends BaseResponse> listApis();
+    Map<String, Class<?>> getApiNameCmdClassMapping();
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2c04dad5/client/pom.xml
----------------------------------------------------------------------
diff --git a/client/pom.xml b/client/pom.xml
index b703cbe..1bbae1f 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -32,6 +32,11 @@
     </dependency>
     <dependency>
       <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-plugin-api-discovery</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
       <artifactId>cloud-plugin-user-authenticator-ldap</artifactId>
       <version>${project.version}</version>
     </dependency>

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2c04dad5/client/tomcatconf/api-discovery_commands.properties.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/api-discovery_commands.properties.in b/client/tomcatconf/api-discovery_commands.properties.in
new file mode 100644
index 0000000..49ddfde
--- /dev/null
+++ b/client/tomcatconf/api-discovery_commands.properties.in
@@ -0,0 +1,23 @@
+# 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.
+
+# bitmap of permissions at the end of each classname, 1 = ADMIN, 2 =
+# RESOURCE_DOMAIN_ADMIN, 4 = DOMAIN_ADMIN, 8 = USER
+# Please standardize naming conventions to camel-case (even for acronyms).
+
+# CloudStack API Discovery service command
+listApis=15

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2c04dad5/client/tomcatconf/components.xml.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/components.xml.in b/client/tomcatconf/components.xml.in
index a7378bd..b9feed1 100755
--- a/client/tomcatconf/components.xml.in
+++ b/client/tomcatconf/components.xml.in
@@ -56,6 +56,9 @@ under the License.
         <adapters key="org.apache.cloudstack.acl.APIAccessChecker">
             <adapter name="StaticRoleBasedAPIAccessChecker" class="org.apache.cloudstack.acl.StaticRoleBasedAPIAccessChecker"/>
         </adapters>
+        <adapters key="org.apache.cloudstack.discovery.ApiDiscoveryService">
+            <adapter name="ApiDiscoveryService" class="org.apache.cloudstack.discovery.ApiDiscoveryServiceImpl"/>
+        </adapters>
         <adapters key="com.cloud.agent.manager.allocator.HostAllocator">
             <adapter name="FirstFitRouting" class="com.cloud.agent.manager.allocator.impl.FirstFitRoutingAllocator"/>
             <!--adapter name="FirstFitRouting" class="com.cloud.agent.manager.allocator.impl.RecreateHostAllocator"/-->
@@ -177,6 +180,7 @@ under the License.
         </adapters>
         <manager name="OvsTunnelManager" key="com.cloud.network.ovs.OvsTunnelManager" class="com.cloud.network.ovs.OvsTunnelManagerImpl"/>
         <manager name="ElasticLoadBalancerManager" key="com.cloud.network.lb.ElasticLoadBalancerManager" class="com.cloud.network.lb.ElasticLoadBalancerManagerImpl"/>
+        <pluggableservice name="ApiDiscoveryService" key="org.apache.cloudstack.discovery.ApiDiscoveryService" class="org.apache.cloudstack.discovery.ApiDiscoveryServiceImpl"/>
         <pluggableservice name="VirtualRouterElementService" key="com.cloud.network.element.VirtualRouterElementService" class="com.cloud.network.element.VirtualRouterElement"/>
         <pluggableservice name="NiciraNvpElementService" key="com.cloud.network.element.NiciraNvpElementService" class="com.cloud.network.element.NiciraNvpElement"/>
         <dao name="OvsTunnelInterfaceDao" class="com.cloud.network.ovs.dao.OvsTunnelInterfaceDaoImpl" singleton="false"/>

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2c04dad5/plugins/api/discovery/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/api/discovery/pom.xml b/plugins/api/discovery/pom.xml
new file mode 100644
index 0000000..a61b275
--- /dev/null
+++ b/plugins/api/discovery/pom.xml
@@ -0,0 +1,44 @@
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+  http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>cloud-plugin-api-discovery</artifactId>
+    <name>Apache CloudStack Plugin - API Discovery</name>
+    <parent>
+        <groupId>org.apache.cloudstack</groupId>
+        <artifactId>cloudstack-plugins</artifactId>
+        <version>4.1.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.cloudstack</groupId>
+            <artifactId>cloud-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cloudstack</groupId>
+            <artifactId>cloud-utils</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2c04dad5/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java
----------------------------------------------------------------------
diff --git a/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java b/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java
new file mode 100644
index 0000000..a7e60e3
--- /dev/null
+++ b/plugins/api/discovery/src/org/apache/cloudstack/api/command/user/discovery/ListApisCmd.java
@@ -0,0 +1,55 @@
+// 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 org.apache.cloudstack.api.command.user.discovery;
+
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.BaseListCmd;
+import org.apache.cloudstack.api.PlugService;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.discovery.ApiDiscoveryService;
+import org.apache.cloudstack.api.response.ApiDiscoveryResponse;
+
+import org.apache.log4j.Logger;
+
+@APICommand(name = "listApis", responseObject = ApiDiscoveryResponse.class, description = "lists all apis available to the user as per their account type", since = "4.1.0")
+public class ListApisCmd extends BaseListCmd {
+
+    public static final Logger s_logger = Logger.getLogger(ListApisCmd.class.getName());
+    private static final String s_name = "listapisresponse";
+
+    @PlugService
+    ApiDiscoveryService _apiDiscoveryService;
+
+    @Override
+    public void execute() throws ServerApiException {
+        if (_apiDiscoveryService != null) {
+            ListResponse<ApiDiscoveryResponse> response = (ListResponse<ApiDiscoveryResponse>) _apiDiscoveryService.listApis();
+            if (response == null) {
+                throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Api Discovery plugin was unable to find and process any apis");
+            }
+            response.setResponseName(getCommandName());
+            this.setResponseObject(response);
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2c04dad5/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
new file mode 100644
index 0000000..b6a4f12
--- /dev/null
+++ b/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiDiscoveryResponse.java
@@ -0,0 +1,75 @@
+// 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 org.apache.cloudstack.api.response;
+
+import com.cloud.user.Account;
+import org.apache.cloudstack.api.ApiConstants;
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+import org.apache.cloudstack.api.BaseResponse;
+import org.apache.cloudstack.api.EntityReference;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@SuppressWarnings("unused")
+@EntityReference(value = Account.class)
+public class ApiDiscoveryResponse extends BaseResponse {
+    @SerializedName(ApiConstants.NAME) @Param(description="the name of the api command")
+    private String name;
+
+    @SerializedName(ApiConstants.DESCRIPTION) @Param(description="description of the api")
+    private String description;
+
+    @SerializedName(ApiConstants.SINCE) @Param(description="version of CloudStack the api was introduced in")
+    private String since;
+
+    @SerializedName(ApiConstants.IS_ASYNC) @Param(description="true if api is asynchronous")
+    private Boolean isAsync;
+
+    @SerializedName(ApiConstants.PARAMS)  @Param(description="the list params the api accepts", responseObject = ApiParameterResponse.class)
+    private Set<ApiParameterResponse> params;
+
+    public ApiDiscoveryResponse(){
+        params = new HashSet<ApiParameterResponse>();
+        isAsync = false;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public void setSince(String since) {
+        this.since = since;
+    }
+
+    public void setAsync(Boolean isAsync) {
+        this.isAsync = isAsync;
+    }
+
+    public void setParams(Set<ApiParameterResponse> params) {
+        this.params = params;
+    }
+
+    public void addParam(ApiParameterResponse param) {
+        this.params.add(param);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2c04dad5/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiParameterResponse.java
----------------------------------------------------------------------
diff --git a/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiParameterResponse.java b/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiParameterResponse.java
new file mode 100644
index 0000000..9a73bec
--- /dev/null
+++ b/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiParameterResponse.java
@@ -0,0 +1,70 @@
+// 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 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.BaseResponse;
+
+public class ApiParameterResponse extends BaseResponse {
+    @SerializedName(ApiConstants.NAME) @Param(description="the name of the api parameter")
+    private String name;
+
+    @SerializedName(ApiConstants.DESCRIPTION) @Param(description="description of the api parameter")
+    private String description;
+
+    @SerializedName(ApiConstants.TYPE) @Param(description="parameter type")
+    private String type;
+
+    @SerializedName(ApiConstants.LENGTH) @Param(description="length of the parameter")
+    private int length;
+
+    @SerializedName(ApiConstants.REQUIRED) @Param(description="version of CloudStack the api was introduced in")
+    private Boolean required;
+
+    @SerializedName(ApiConstants.SINCE) @Param(description="version of CloudStack the api was introduced in")
+    private String since;
+
+    public ApiParameterResponse(){
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public void setLength(int length) {
+        this.length = length;
+    }
+
+    public void setRequired(Boolean required) {
+        this.required = required;
+    }
+
+    public void setSince(String since) {
+        this.since = since;
+    }
+
+ }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2c04dad5/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
new file mode 100644
index 0000000..ca57df5
--- /dev/null
+++ b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java
@@ -0,0 +1,144 @@
+// 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 org.apache.cloudstack.discovery;
+
+import com.cloud.utils.ReflectUtil;
+import com.cloud.utils.component.AdapterBase;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseAsyncCreateCmd;
+import org.apache.cloudstack.api.BaseResponse;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.ApiDiscoveryResponse;
+import org.apache.cloudstack.api.response.ApiParameterResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Local(value = ApiDiscoveryService.class)
+public class ApiDiscoveryServiceImpl extends AdapterBase implements ApiDiscoveryService {
+
+    private static final Logger s_logger = Logger.getLogger(ApiDiscoveryServiceImpl.class);
+    private Map<String, Class<?>> _apiNameCmdClassMap;
+    private ListResponse<ApiDiscoveryResponse> _discoveryResponse;
+
+    protected ApiDiscoveryServiceImpl() {
+        super();
+    }
+
+    private void generateApiNameCmdClassMapping() {
+        _apiNameCmdClassMap = new HashMap<String, Class<?>>();
+        Set<Class<?>> cmdClasses = ReflectUtil.getClassesWithAnnotation(APICommand.class, new String[]{"org.apache.cloudstack.api", "com.cloud.api"});
+
+        for(Class<?> cmdClass: cmdClasses) {
+            String apiName = cmdClass.getAnnotation(APICommand.class).name();
+            if (_apiNameCmdClassMap.containsKey(apiName)) {
+                s_logger.error("API Cmd class " + cmdClass.getName() + " has non-unique apiname" + apiName);
+                continue;
+            }
+            _apiNameCmdClassMap.put(apiName, cmdClass);
+        }
+    }
+
+    private void precacheListApiResponse() {
+
+        if(_apiNameCmdClassMap == null)
+            return;
+
+        _discoveryResponse = new ListResponse<ApiDiscoveryResponse>();
+
+        List<ApiDiscoveryResponse> apiDiscoveryResponses = new ArrayList<ApiDiscoveryResponse>();
+
+        for(String key: _apiNameCmdClassMap.keySet()) {
+            Class<?> cmdClass = _apiNameCmdClassMap.get(key);
+            APICommand apiCmdAnnotation = cmdClass.getAnnotation(APICommand.class);
+            if (apiCmdAnnotation == null)
+                apiCmdAnnotation = cmdClass.getSuperclass().getAnnotation(APICommand.class);
+            if (apiCmdAnnotation == null
+                    || !apiCmdAnnotation.includeInApiDoc()
+                    || apiCmdAnnotation.name().isEmpty())
+                continue;
+
+            ApiDiscoveryResponse response = new ApiDiscoveryResponse();
+            response.setName(apiCmdAnnotation.name());
+            response.setDescription(apiCmdAnnotation.description());
+            response.setSince(apiCmdAnnotation.since());
+
+            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());
+                    paramResponse.setLength(parameterAnnotation.length());
+                    paramResponse.setRequired(parameterAnnotation.required());
+                    paramResponse.setSince(parameterAnnotation.since());
+                    response.addParam(paramResponse);
+                }
+            }
+            response.setObjectName("apis");
+            apiDiscoveryResponses.add(response);
+        }
+        _discoveryResponse.setResponses(apiDiscoveryResponses);
+    }
+
+    @Override
+    public boolean configure(String name, Map<String, Object> params)
+            throws ConfigurationException {
+        super.configure(name, params);
+
+        generateApiNameCmdClassMapping();
+        precacheListApiResponse();
+
+        return true;
+    }
+
+    public Map<String, Class<?>> getApiNameCmdClassMapping() {
+        return _apiNameCmdClassMap;
+    }
+
+    @Override
+    public ListResponse<? extends BaseResponse> listApis() {
+        return _discoveryResponse;
+    }
+
+    @Override
+    public String[] getPropertiesFiles() {
+        return new String[]{"api-discovery_commands.properties"};
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2c04dad5/plugins/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/pom.xml b/plugins/pom.xml
index f0589a1..a42ae29 100644
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -32,6 +32,7 @@
     <testSourceDirectory>test</testSourceDirectory>
   </build>
   <modules>
+    <module>api/discovery</module>
     <module>acl/static-role-based</module>
     <module>deployment-planners/user-concentrated-pod</module>
     <module>deployment-planners/user-dispersing</module>

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/2c04dad5/server/src/com/cloud/api/ApiServer.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java
index e2dc9ce..8d304e0 100755
--- a/server/src/com/cloud/api/ApiServer.java
+++ b/server/src/com/cloud/api/ApiServer.java
@@ -50,6 +50,7 @@ import javax.crypto.spec.SecretKeySpec;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
+import com.cloud.utils.ReflectUtil;
 import org.apache.cloudstack.acl.APIAccessChecker;
 import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.api.*;
@@ -59,6 +60,7 @@ import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
 import org.apache.cloudstack.api.command.user.vm.ListVMsCmd;
 import org.apache.cloudstack.api.command.user.vmgroup.ListVMGroupsCmd;
 import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
+import org.apache.cloudstack.discovery.ApiDiscoveryService;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.http.client.utils.URLEncodedUtils;
 import org.apache.http.ConnectionClosedException;
@@ -145,8 +147,11 @@ public class ApiServer implements HttpRequestHandler {
     @Inject private AccountManager _accountMgr = null;
     @Inject private DomainManager _domainMgr = null;
     @Inject private AsyncJobManager _asyncMgr = null;
+
     @Inject(adapter = APIAccessChecker.class)
     protected Adapters<APIAccessChecker> _apiAccessCheckers;
+    @Inject(adapter = ApiDiscoveryService.class)
+    protected Adapters<ApiDiscoveryService> _apiDiscoveryServices;
 
     private Account _systemAccount = null;
     private User _systemUser = null;
@@ -201,17 +206,8 @@ public class ApiServer implements HttpRequestHandler {
             }
         }
 
-        // Populate api name and cmd class mappings
-        Reflections reflections = new Reflections("org.apache.cloudstack.api");
-        Set<Class<?>> cmdClasses = reflections.getTypesAnnotatedWith(APICommand.class);
-        reflections = new Reflections("com.cloud.api");
-        cmdClasses.addAll(reflections.getTypesAnnotatedWith(APICommand.class));
-        for(Class<?> cmdClass: cmdClasses) {
-            String apiName = cmdClass.getAnnotation(APICommand.class).name();
-            if (_apiNameCmdClassMap.containsKey(apiName)) {
-                s_logger.error("API Cmd class " + cmdClass.getName() + " has non-unique apiname" + apiName);
-            }
-            _apiNameCmdClassMap.put(apiName, cmdClass);
+        for (ApiDiscoveryService discoveryService: _apiDiscoveryServices) {
+            _apiNameCmdClassMap.putAll(discoveryService.getApiNameCmdClassMapping());
         }
 
         encodeApiResponse = Boolean.valueOf(configDao.getValue(Config.EncodeApiResponse.key()));