diff --git a/api/src/com/cloud/user/AccountService.java b/api/src/com/cloud/user/AccountService.java
index ce16f5ee063..9f5f4d225e0 100755
--- a/api/src/com/cloud/user/AccountService.java
+++ b/api/src/com/cloud/user/AccountService.java
@@ -20,6 +20,7 @@ import java.util.List;
import java.util.Map;
import org.apache.cloudstack.acl.ControlledEntity;
+import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
@@ -193,6 +194,8 @@ public interface AccountService {
UserAccount getUserByApiKey(String apiKey);
+ RoleType getRoleType(Account account);
+
void checkAccess(Account account, Domain domain) throws PermissionDeniedException;
void checkAccess(Account account, AccessType accessType, boolean sameOwner, ControlledEntity... entities) throws PermissionDeniedException;
diff --git a/api/src/org/apache/cloudstack/acl/APIAccessChecker.java b/api/src/org/apache/cloudstack/acl/APIChecker.java
similarity index 67%
rename from api/src/org/apache/cloudstack/acl/APIAccessChecker.java
rename to api/src/org/apache/cloudstack/acl/APIChecker.java
index 3194bd11d17..61dd7de75cb 100644
--- a/api/src/org/apache/cloudstack/acl/APIAccessChecker.java
+++ b/api/src/org/apache/cloudstack/acl/APIChecker.java
@@ -16,17 +16,13 @@
// under the License.
package org.apache.cloudstack.acl;
-import java.util.Properties;
-
-import com.cloud.exception.PermissionDeniedException;
-import com.cloud.user.Account;
-import com.cloud.user.User;
+import org.apache.cloudstack.acl.RoleType;
import com.cloud.utils.component.Adapter;
-/**
- * APIAccessChecker checks the ownership and access control to API requests
- */
-public interface APIAccessChecker extends Adapter {
- // Interface for checking access to an API for an user
- boolean canAccessAPI(User user, String apiCommandName) throws PermissionDeniedException;
+// APIChecker checks the ownership and access control to API requests
+public interface APIChecker extends Adapter {
+ // Interface for checking access for a role using apiname
+ boolean checkAccess(RoleType roleType, String apiCommandName);
+ // Interface for checking existence of an api by name
+ boolean checkExistence(String apiCommandName);
}
diff --git a/api/src/org/apache/cloudstack/acl/RoleType.java b/api/src/org/apache/cloudstack/acl/RoleType.java
new file mode 100644
index 00000000000..0d1c4460c1e
--- /dev/null
+++ b/api/src/org/apache/cloudstack/acl/RoleType.java
@@ -0,0 +1,37 @@
+// 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.acl;
+
+// Enum for default roles in CloudStack
+public enum RoleType {
+
+ Admin(1),
+ ResourceAdmin(2),
+ DomainAdmin(4),
+ User(8),
+ Unknown(0);
+
+ private int mask;
+
+ private RoleType(int mask) {
+ this.mask = mask;
+ }
+
+ public int getValue() {
+ return mask;
+ }
+}
diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java
index b4ce24c2bc9..d3bfcd66afc 100644
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -158,6 +158,7 @@ public class ApiConstants {
public static final String RECEIVED_BYTES = "receivedbytes";
public static final String REQUIRES_HVM = "requireshvm";
public static final String RESOURCE_TYPE = "resourcetype";
+ public static final String RESPONSE = "response";
public static final String QUERY_FILTER = "queryfilter";
public static final String SCHEDULE = "schedule";
public static final String SCOPE = "scope";
diff --git a/client/tomcatconf/api-discovery_commands.properties.in b/client/tomcatconf/api-discovery_commands.properties.in
deleted file mode 100644
index 49ddfde42d8..00000000000
--- a/client/tomcatconf/api-discovery_commands.properties.in
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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
diff --git a/client/tomcatconf/components.xml.in b/client/tomcatconf/components.xml.in
index b779c860cc2..bb39839c820 100755
--- a/client/tomcatconf/components.xml.in
+++ b/client/tomcatconf/components.xml.in
@@ -53,7 +53,7 @@ under the License.
true
-
+
diff --git a/docs/en-US/accessing-vms.xml b/docs/en-US/accessing-vms.xml
index d69d021471b..c77ad4eee52 100644
--- a/docs/en-US/accessing-vms.xml
+++ b/docs/en-US/accessing-vms.xml
@@ -29,7 +29,7 @@
Log in to the &PRODUCT; UI as a user or admin.
Click Instances, then click the name of a running VM.
- Click the View Console button .
+ Click the View Console button .
To access a VM directly over the network:
diff --git a/docs/en-US/autoscale.xml b/docs/en-US/autoscale.xml
new file mode 100644
index 00000000000..d63281f9e7e
--- /dev/null
+++ b/docs/en-US/autoscale.xml
@@ -0,0 +1,284 @@
+
+
+%BOOK_ENTITIES;
+]>
+
+
+
+ Configuring AutoScale
+ AutoScaling allows you to scale your back-end services or application VMs up or down
+ seamlessly and automatically according to the conditions you define. With AutoScaling enabled,
+ you can ensure that the number of VMs you are using seamlessly scale up when demand increases,
+ and automatically decreases when demand subsides. Thus it helps you save compute costs by
+ terminating underused VMs automatically and launching new VMs when you need them, without the
+ need for manual intervention.
+ NetScaler AutoScaling is designed to seamlessly launch or terminate VMs based on
+ user-defined conditions. Conditions for triggering a scaleup or scaledown action can vary from a
+ simple use case like monitoring the CPU usage of a server to a complex use case of monitoring a
+ combination of server's responsiveness and its CPU usage. For example, you can configure
+ AutoScaling to launch an additional VM whenever CPU usage exceeds 80 percent for 15 minutes, or
+ to remove a VM whenever CPU usage is less than 20 percent for 30 minutes.
+ &PRODUCT; uses the NetScaler load balancer to monitor all aspects of a system's health and
+ work in unison with &PRODUCT; to initiate scale-up or scale-down actions. The supported
+ NetScaler version is 10.0.
+
+ Prerequisites
+ Before you configure an AutoScale rule, consider the following:
+
+
+
+ Ensure that the necessary template is prepared before configuring AutoScale. When a VM
+ is deployed by using a template and when it comes up, the application should be up and
+ running.
+
+ If the application is not running, the NetScaler device considers the VM as
+ ineffective and continues provisioning the VMs unconditionally until the resource limit is
+ exhausted.
+
+
+
+ Deploy the templates you prepared. Ensure that the applications come up on the first
+ boot and is ready to take the traffic. Observe the time requires to deploy the template.
+ Consider this time when you specify the quiet time while configuring AutoScale.
+
+
+ The AutoScale feature supports the SNMP counters that can be used to define conditions
+ for taking scale up or scale down actions. To monitor the SNMP-based counter, ensure that
+ the SNMP agent is installed in the template used for creating the AutoScale VMs, and the
+ SNMP operations work with the configured SNMP community and port by using standard SNMP
+ managers. For example, see to configure SNMP on a RHEL
+ machine.
+
+
+ Ensure that the endpointe.url parameter present in the Global Settings is set to the
+ Management Server API URL. For example, http://10.102.102.22:8080/client/api. In a
+ multi-node Management Server deployment, use the virtual IP address configured in the load
+ balancer for the management server’s cluster. Additionally, ensure that the NetScaler device
+ has access to this IP address to provide AutoScale support.
+ If you update the endpointe.url, disable the AutoScale functionality of the load
+ balancer rules in the system, then enable them back to reflect the changes. For more
+ information see
+
+
+ If the API Key and Secret Key are regenerated for an AutoScale user, ensure that the
+ AutoScale functionality of the load balancers that the user participates in are disabled and
+ then enabled to reflect the configuration changes in the NetScaler.
+
+
+ In an advanced Zone, ensure that at least one VM should be present before configuring a
+ load balancer rule with AutoScale. Having one VM in the network ensures that the network is
+ in implemented state for configuring AutoScale.
+
+
+
+ Configuration
+ Specify the following:
+
+
+
+
+
+
+ autoscaleateconfig.png: Configuring AutoScale
+
+
+
+
+ Template: A template consists of a base OS image and
+ application. A template is used to provision the new instance of an application on a scaleup
+ action. When a VM is deployed from a template, the VM can start taking the traffic from the
+ load balancer without any admin intervention. For example, if the VM is deployed for a Web
+ service, it should have the Web server running, the database connected, and so on.
+
+
+ Compute offering: A predefined set of virtual hardware
+ attributes, including CPU speed, number of CPUs, and RAM size, that the user can select when
+ creating a new virtual machine instance. Choose one of the compute offerings to be used
+ while provisioning a VM instance as part of scaleup action.
+
+
+ Min Instance: The minimum number of active VM instances
+ that is assigned to a load balancing rule. The active VM instances are the application
+ instances that are up and serving the traffic, and are being load balanced. This parameter
+ ensures that a load balancing rule has at least the configured number of active VM instances
+ are available to serve the traffic.
+
+ If an application, such as SAP, running on a VM instance is down for some reason, the
+ VM is then not counted as part of Min Instance parameter, and the AutoScale feature
+ initiates a scaleup action if the number of active VM instances is below the configured
+ value. Similarly, when an application instance comes up from its earlier down state, this
+ application instance is counted as part of the active instance count and the AutoScale
+ process initiates a scaledown action when the active instance count breaches the Max
+ instance value.
+
+
+
+ Max Instance: Maximum number of active VM instances
+ that should be assigned to a load balancing rule. This
+ parameter defines the upper limit of active VM instances that can be assigned to a load
+ balancing rule.
+ Specifying a large value for the maximum instance parameter might result in provisioning
+ large number of VM instances, which in turn leads to a single load balancing rule exhausting
+ the VM instances limit specified at the account or domain level.
+
+ If an application, such as SAP, running on a VM instance is down for some reason, the
+ VM is not counted as part of Max Instance parameter. So there may be scenarios where the
+ number of VMs provisioned for a scaleup action might be more than the configured Max
+ Instance value. Once the application instances in the VMs are up from an earlier down
+ state, the AutoScale feature starts aligning to the configured Max Instance value.
+
+
+
+ Specify the following scale-up and scale-down policies:
+
+
+ Duration: The duration, in seconds, for which the
+ conditions you specify must be true to trigger a scaleup action. The conditions defined
+ should hold true for the entire duration you specify for an AutoScale action to be invoked.
+
+
+
+ Counter: The performance counters expose the state of
+ the monitored instances. By default, &PRODUCT; offers four performance counters: Three SNMP
+ counters and one NetScaler counter. The SNMP counters are Linux User CPU, Linux System CPU,
+ and Linux CPU Idle. The NetScaler counter is ResponseTime. The root administrator can add
+ additional counters into &PRODUCT; by using the &PRODUCT; API.
+
+
+ Operator: The following five relational operators are
+ supported in AutoScale feature: Greater than, Less than, Less than or equal to, Greater than
+ or equal to, and Equal to.
+
+
+ Threshold: Threshold value to be used for the counter.
+ Once the counter defined above breaches the threshold value, the AutoScale feature initiates
+ a scaleup or scaledown action.
+
+
+ Add: Click Add to add the condition.
+
+
+ Additionally, if you want to configure the advanced settings, click Show advanced settings,
+ and specify the following:
+
+
+ Polling interval: Frequency in which the conditions,
+ combination of counter, operator and threshold, are to be evaluated before taking a scale up
+ or down action. The default polling interval is 30 seconds.
+
+
+ Quiet Time: This is the cool down period after an
+ AutoScale action is initiated. The time includes the time taken to complete provisioning a
+ VM instance from its template and the time taken by an application to be ready to serve
+ traffic. This quiet time allows the fleet to come up to a stable state before any action can
+ take place. The default is 300 seconds.
+
+
+ Destroy VM Grace Period: The duration in seconds, after
+ a scaledown action is initiated, to wait before the VM is destroyed as part of scaledown
+ action. This is to ensure graceful close of any pending sessions or transactions being
+ served by the VM marked for destroy. The default is 120 seconds.
+
+
+ Security Groups: Security groups provide a way to
+ isolate traffic to the VM instances. A security group is a group of VMs that filter their
+ incoming and outgoing traffic according to a set of rules, called ingress and egress rules.
+ These rules filter network traffic according to the IP address that is attempting to
+ communicate with the VM.
+
+
+ Disk Offerings: A predefined set of disk size for
+ primary data storage.
+
+
+ SNMP Community: The SNMP community string to be used by
+ the NetScaler device to query the configured counter value from the provisioned VM
+ instances. Default is public.
+
+
+ SNMP Port: The port number on which the SNMP agent that
+ run on the provisioned VMs is listening. Default port is 161.
+
+
+ User: This is the user that the NetScaler device use to
+ invoke scaleup and scaledown API calls to the cloud. If no option is specified, the user who
+ configures AutoScaling is applied. Specify another user name to override.
+
+
+ Apply: Click Apply to create the AutoScale
+ configuration.
+
+
+
+ Disabling and Enabling an AutoScale Configuration
+ If you want to perform any maintenance operation on the AutoScale VM instances, disable
+ the AutoScale configuration. When the AutoScale configuration is disabled, no scaleup or
+ scaledown action is performed. You can use this downtime for the maintenance activities. To
+ disable the AutoScale configuration, click the Disable AutoScale
+
+
+
+
+ EnableDisable.png: button to enable or disable AutoScale.
+
+ button.
+
+ The button toggles between enable and disable, depending on whether AutoScale is currently
+ enabled or not. After the maintenance operations are done, you can enable the AutoScale
+ configuration back. To enable, open the AutoScale configuration page again, then click the
+ Enable AutoScale
+
+
+
+
+ EnableDisable.png: button to enable or disable AutoScale.
+
+ button.
+
+ Updating an AutoScale Configuration
+ You can update the various parameters and add or delete the conditions in a scaleup or
+ scaledown rule. Before you update an AutoScale configuration, ensure that you disable the
+ AutoScale load balancer rule by clicking the Disable AutoScale button.
+
+ After you modify the required AutoScale parameters, click Apply. To apply the new AutoScale
+ policies, open the AutoScale configuration page again, then click the Enable AutoScale
+ button.
+
+ Runtime Considerations
+
+
+
+
+ An administrator should not assign a VM to a load balancing rule which is configured for
+ AutoScale.
+
+
+ Before a VM provisioning is completed if NetScaler is shutdown or restarted, the
+ provisioned VM cannot be a part of the load balancing rule though the intent was to assign
+ it to a load balancing rule. To workaround, rename the AutoScale provisioned VMs based on
+ the rule name or ID so at any point of time the VMs can be reconciled to its load balancing
+ rule.
+
+
+ Making API calls outside the context of AutoScale, such as destroyVM, on an autoscaled
+ VM leaves the load balancing configuration in an inconsistent state. Though VM is destroyed
+ from the load balancer rule, NetScaler continues to show the VM as a service assigned to a
+ rule.
+
+
+
diff --git a/docs/en-US/building-marvin.xml b/docs/en-US/building-marvin.xml
new file mode 100644
index 00000000000..3dac9d65d60
--- /dev/null
+++ b/docs/en-US/building-marvin.xml
@@ -0,0 +1,46 @@
+
+
+%BOOK_ENTITIES;
+]>
+
+
+
+
+ Building and Installing Marvin
+ Marvin is built with Maven and is dependent on APIdoc. To build it do the following in the root tree of &PRODUCT;:
+ mvn -P developer -l :cloud-apidoc
+ mvn -P developer -l :cloud-marvin
+ If successfull the build will have created the cloudstackAPI Python package under tools/marvin/marvin/cloudstackAPI as well as a gziped Marvin package under tools/marvin dist. To install the Python Marvin module do the following in tools/marvin:
+ sudo python ./setup.py install
+ The dependencies will be downloaded the Python module installed and you should be able to use Marvin in Python. Check that you can import the module before starting to use it.
+ $ python
+Python 2.7.3 (default, Nov 17 2012, 19:54:34)
+[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
+Type "help", "copyright", "credits" or "license" for more information.
+>>> import marvin
+>>> from marvin.cloudstackAPI import *
+>>>
+
+ You could also install it using pip or easy_install using the local distribution package in tools/marvin/dist :
+ pip install tools/marvin/dist/Marvin-0.1.0.tar.gz
+ Or:
+ easy_install tools/marvin/dist/Marvin-0.1.0.tar.gz
+
+
diff --git a/docs/en-US/configure-snmp-rhel.xml b/docs/en-US/configure-snmp-rhel.xml
new file mode 100644
index 00000000000..bd227ff8ed5
--- /dev/null
+++ b/docs/en-US/configure-snmp-rhel.xml
@@ -0,0 +1,86 @@
+
+
+%BOOK_ENTITIES;
+]>
+
+
diff --git a/docs/en-US/external-firewalls-and-load-balancers.xml b/docs/en-US/external-firewalls-and-load-balancers.xml
index 1452804885d..6ca49f0ef03 100644
--- a/docs/en-US/external-firewalls-and-load-balancers.xml
+++ b/docs/en-US/external-firewalls-and-load-balancers.xml
@@ -3,26 +3,31 @@
%BOOK_ENTITIES;
]>
-
-
- External Firewalls and Load Balancers
- &PRODUCT; is capable of replacing its Virtual Router with an external Juniper SRX device and an optional external NetScaler or F5 load balancer for gateway and load balancing services. In this case, the VMs use the SRX as their gateway.
+ External Firewalls and Load Balancers
+ &PRODUCT; is capable of replacing its Virtual Router with an external Juniper SRX device and
+ an optional external NetScaler or F5 load balancer for gateway and load balancing services. In
+ this case, the VMs use the SRX as their gateway.
+
+
+
+
+
diff --git a/docs/en-US/images/view-console-button.png b/docs/en-US/images/view-console-button.png
new file mode 100644
index 00000000000..b321ceadefe
Binary files /dev/null and b/docs/en-US/images/view-console-button.png differ
diff --git a/docs/en-US/marvin.xml b/docs/en-US/marvin.xml
index 062616ac888..8fd2c96fe3f 100644
--- a/docs/en-US/marvin.xml
+++ b/docs/en-US/marvin.xml
@@ -29,4 +29,5 @@
Marvin's complete documenation is on the wiki at https://cwiki.apache.org/CLOUDSTACK/testing-with-python.html
The source code is located at tools/marvin
+
diff --git a/docs/en-US/ongoing-configuration-of-external-firewalls-loadbalancer.xml b/docs/en-US/ongoing-configuration-of-external-firewalls-loadbalancer.xml
new file mode 100644
index 00000000000..c90c7ada622
--- /dev/null
+++ b/docs/en-US/ongoing-configuration-of-external-firewalls-loadbalancer.xml
@@ -0,0 +1,46 @@
+
+
+%BOOK_ENTITIES;
+]>
+
+
+ Ongoing Configuration of External Firewalls and Load Balancers
+ Additional user actions (e.g. setting a port forward) will cause further programming of the
+ firewall and load balancer. A user may request additional public IP addresses and forward
+ traffic received at these IPs to specific VMs. This is accomplished by enabling static NAT for a
+ public IP address, assigning the IP to a VM, and specifying a set of protocols and port ranges
+ to open. When a static NAT rule is created, &PRODUCT; programs the zone's external firewall with
+ the following objects:
+
+
+ A static NAT rule that maps the public IP address to the private IP address of a
+ VM.
+
+
+ A security policy that allows traffic within the set of protocols and port ranges that
+ are specified.
+
+
+ A firewall filter counter that measures the number of bytes of incoming traffic to the
+ public IP.
+
+
+ The number of incoming and outgoing bytes through source NAT, static NAT, and load balancing
+ rules is measured and saved on each external element. This data is collected on a regular basis
+ and stored in the &PRODUCT; database.
+
diff --git a/docs/en-US/system-service-offerings.xml b/docs/en-US/system-service-offerings.xml
index c41aa2e293b..84d5f7ae7b5 100644
--- a/docs/en-US/system-service-offerings.xml
+++ b/docs/en-US/system-service-offerings.xml
@@ -26,4 +26,5 @@
System Service Offerings
System service offerings provide a choice of CPU speed, number of CPUs, tags, and RAM size, just as other service offerings do. But rather than being used for virtual machine instances and exposed to users, system service offerings are used to change the default properties of virtual routers, console proxies, and other system VMs. System service offerings are visible only to the &PRODUCT; root administrator. &PRODUCT; provides default system service offerings. The &PRODUCT; root administrator can create additional custom system service offerings.
When &PRODUCT; creates a virtual router for a guest network, it uses default settings which are defined in the system service offering associated with the network offering. You can upgrade the capabilities of the virtual router by applying a new network offering that contains a different system service offering. All virtual routers in that network will begin using the settings from the new service offering.
+
diff --git a/plugins/acl/static-role-based/src/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java b/plugins/acl/static-role-based/src/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java
index d39f87f1048..740fbbc6456 100644
--- a/plugins/acl/static-role-based/src/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java
+++ b/plugins/acl/static-role-based/src/org/apache/cloudstack/acl/StaticRoleBasedAPIAccessChecker.java
@@ -16,93 +16,50 @@
// under the License.
package org.apache.cloudstack.acl;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.*;
+import com.cloud.server.ManagementServer;
+import com.cloud.utils.component.AdapterBase;
+import com.cloud.utils.component.ComponentLocator;
+import com.cloud.utils.component.PluggableService;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
-import org.apache.cloudstack.acl.APIAccessChecker;
import org.apache.log4j.Logger;
-import com.cloud.exception.PermissionDeniedException;
-import com.cloud.server.ManagementServer;
-import com.cloud.user.Account;
-import com.cloud.user.AccountManager;
-import com.cloud.user.User;
-import com.cloud.utils.PropertiesUtil;
-import com.cloud.utils.component.AdapterBase;
-import com.cloud.utils.component.ComponentLocator;
-import com.cloud.utils.component.Inject;
-import com.cloud.utils.component.PluggableService;
-
-/*
- * This is the default API access checker that grab's the user's account
- * based on the account type, access is granted referring to commands in all *.properties files.
- */
-
-@Local(value=APIAccessChecker.class)
-public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIAccessChecker {
+// This is the default API access checker that grab's the user's account
+// based on the account type, access is granted
+@Local(value=APIChecker.class)
+public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIChecker {
protected static final Logger s_logger = Logger.getLogger(StaticRoleBasedAPIAccessChecker.class);
- public static final short ADMIN_COMMAND = 1;
- public static final short DOMAIN_ADMIN_COMMAND = 4;
- public static final short RESOURCE_DOMAIN_ADMIN_COMMAND = 2;
- public static final short USER_COMMAND = 8;
- private static List s_userCommands = null;
- private static List s_resellerCommands = null; // AKA domain-admin
- private static List s_adminCommands = null;
- private static List s_resourceDomainAdminCommands = null;
- private static List s_allCommands = null;
- protected @Inject AccountManager _accountMgr;
+ private static Map> s_roleBasedApisMap =
+ new HashMap>();
protected StaticRoleBasedAPIAccessChecker() {
super();
- s_allCommands = new ArrayList();
- s_userCommands = new ArrayList();
- s_resellerCommands = new ArrayList();
- s_adminCommands = new ArrayList();
- s_resourceDomainAdminCommands = new ArrayList();
+ for (RoleType roleType: RoleType.values()) {
+ s_roleBasedApisMap.put(roleType, new HashSet());
+ }
}
@Override
- public boolean canAccessAPI(User user, String apiCommandName)
- throws PermissionDeniedException{
-
- boolean commandExists = s_allCommands.contains(apiCommandName);
-
- if(commandExists && user != null){
- Long accountId = user.getAccountId();
- Account userAccount = _accountMgr.getAccount(accountId);
- short accountType = userAccount.getType();
- return isCommandAvailableForAccount(accountType, apiCommandName);
- }
-
- return commandExists;
+ public boolean checkAccess(RoleType roleType, String commandName) {
+ return s_roleBasedApisMap.get(roleType).contains(commandName);
}
- private static boolean isCommandAvailableForAccount(short accountType, String commandName) {
- boolean isCommandAvailable = false;
- switch (accountType) {
- case Account.ACCOUNT_TYPE_ADMIN:
- isCommandAvailable = s_adminCommands.contains(commandName);
- break;
- case Account.ACCOUNT_TYPE_DOMAIN_ADMIN:
- isCommandAvailable = s_resellerCommands.contains(commandName);
- break;
- case Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN:
- isCommandAvailable = s_resourceDomainAdminCommands.contains(commandName);
- break;
- case Account.ACCOUNT_TYPE_NORMAL:
- isCommandAvailable = s_userCommands.contains(commandName);
- break;
+ @Override
+ public boolean checkExistence(String apiName) {
+ for (RoleType roleType: RoleType.values()) {
+ if (s_roleBasedApisMap.get(roleType).contains(apiName))
+ return true;
}
- return isCommandAvailable;
+ return false;
}
@Override
@@ -114,69 +71,28 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIA
List services = locator.getAllPluggableServices();
services.add((PluggableService) ComponentLocator.getComponent(ManagementServer.Name));
- List configFiles = new ArrayList();
+ Map configPropertiesMap = new HashMap();
for (PluggableService service : services) {
- configFiles.addAll(Arrays.asList(service.getPropertiesFiles()));
+ configPropertiesMap.putAll(service.getProperties());
}
- processConfigFiles(configFiles);
+ processConfigFiles(configPropertiesMap);
return true;
}
- private void processConfigFiles(List configFiles) {
- Properties preProcessedCommands = new Properties();
-
- for (String configFile : configFiles) {
- File commandsFile = PropertiesUtil.findConfigFile(configFile);
- if (commandsFile != null) {
- try {
- preProcessedCommands.load(new FileInputStream(commandsFile));
- } catch (FileNotFoundException fnfex) {
- // in case of a file within a jar in classpath, try to open stream using url
- InputStream stream = PropertiesUtil.openStreamFromURL(configFile);
- if (stream != null) {
- try {
- preProcessedCommands.load(stream);
- } catch (IOException e) {
- s_logger.error("IO Exception, unable to find properties file:", fnfex);
- }
- } else {
- s_logger.error("Unable to find properites file", fnfex);
- }
- } catch (IOException ioe) {
- s_logger.error("IO Exception loading properties file", ioe);
- }
- }
- }
-
- for (Object key : preProcessedCommands.keySet()) {
- String preProcessedCommand = preProcessedCommands.getProperty((String) key);
- int splitIndex = preProcessedCommand.lastIndexOf(";");
- // Backward compatible to old style, apiname=pkg;mask
- String mask = preProcessedCommand.substring(splitIndex+1);
-
+ private void processConfigFiles(Map configMap) {
+ for (Map.Entry entry: configMap.entrySet()) {
+ String apiName = entry.getKey();
+ String roleMask = entry.getValue();
try {
- short cmdPermissions = Short.parseShort(mask);
- if ((cmdPermissions & ADMIN_COMMAND) != 0) {
- s_adminCommands.add((String) key);
+ short cmdPermissions = Short.parseShort(roleMask);
+ for (RoleType roleType: RoleType.values()) {
+ if ((cmdPermissions & roleType.getValue()) != 0)
+ s_roleBasedApisMap.get(roleType).add(apiName);
}
- if ((cmdPermissions & RESOURCE_DOMAIN_ADMIN_COMMAND) != 0) {
- s_resourceDomainAdminCommands.add((String) key);
- }
- if ((cmdPermissions & DOMAIN_ADMIN_COMMAND) != 0) {
- s_resellerCommands.add((String) key);
- }
- if ((cmdPermissions & USER_COMMAND) != 0) {
- s_userCommands.add((String) key);
- }
- s_allCommands.addAll(s_adminCommands);
- s_allCommands.addAll(s_resourceDomainAdminCommands);
- s_allCommands.addAll(s_userCommands);
- s_allCommands.addAll(s_resellerCommands);
} catch (NumberFormatException nfe) {
- s_logger.info("Malformed command.properties permissions value, key = " + key + ", value = " + preProcessedCommand);
+ s_logger.info("Malformed commands.properties permissions value, for entry: " + entry.toString());
}
}
}
-
}
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
index dcbaec1d160..ed3e1751027 100644
--- 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
@@ -16,9 +16,12 @@
// under the License.
package org.apache.cloudstack.api.command.user.discovery;
+import com.cloud.user.UserContext;
+import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
-import org.apache.cloudstack.api.BaseListCmd;
+import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.PlugService;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.ListResponse;
@@ -27,8 +30,8 @@ import org.apache.cloudstack.api.response.ApiDiscoveryResponse;
import org.apache.log4j.Logger;
-@APICommand(name = "listApis", responseObject = ApiDiscoveryResponse.class, description = "lists all available apis on the server, provided by Api Discovery plugin", since = "4.1.0")
-public class ListApisCmd extends BaseListCmd {
+@APICommand(name = "listApis", responseObject = ApiDiscoveryResponse.class, description = "lists all available apis on the server, provided by the Api Discovery plugin", since = "4.1.0")
+public class ListApisCmd extends BaseCmd {
public static final Logger s_logger = Logger.getLogger(ListApisCmd.class.getName());
private static final String s_name = "listapisresponse";
@@ -36,12 +39,16 @@ public class ListApisCmd extends BaseListCmd {
@PlugService
ApiDiscoveryService _apiDiscoveryService;
+ @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, description="API name")
+ private String name;
+
@Override
public void execute() throws ServerApiException {
if (_apiDiscoveryService != null) {
- ListResponse response = (ListResponse) _apiDiscoveryService.listApis();
+ RoleType roleType = _accountService.getRoleType(UserContext.current().getCaller());
+ ListResponse response = (ListResponse) _apiDiscoveryService.listApis(roleType, name);
if (response == null) {
- throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Api Discovery plugin was unable to find and process any apis");
+ throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Api Discovery plugin was unable to find an api by that name or process any apis");
}
response.setResponseName(getCommandName());
this.setResponseObject(response);
@@ -52,4 +59,10 @@ public class ListApisCmd extends BaseListCmd {
public String getCommandName() {
return s_name;
}
+
+ @Override
+ public long getEntityOwnerId() {
+ // no owner is needed for list command
+ return 0;
+ }
}
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 dd1298bfec5..de6a9f93965 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
@@ -16,18 +16,15 @@
// 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;
@@ -41,11 +38,18 @@ public class ApiDiscoveryResponse extends BaseResponse {
@SerializedName(ApiConstants.IS_ASYNC) @Param(description="true if api is asynchronous")
private Boolean isAsync;
+ @SerializedName("related") @Param(description="comma separated related apis")
+ private String related;
+
@SerializedName(ApiConstants.PARAMS) @Param(description="the list params the api accepts", responseObject = ApiParameterResponse.class)
private Set params;
+ @SerializedName(ApiConstants.RESPONSE) @Param(description="api response fields", responseObject = ApiResponseResponse.class)
+ private Set apiResponse;
+
public ApiDiscoveryResponse(){
params = new HashSet();
+ apiResponse = new HashSet();
isAsync = false;
}
@@ -65,6 +69,18 @@ public class ApiDiscoveryResponse extends BaseResponse {
this.isAsync = isAsync;
}
+ public String getRelated() {
+ return related;
+ }
+
+ public void setRelated(String related) {
+ this.related = related;
+ }
+
+ public Set getParams() {
+ return params;
+ }
+
public void setParams(Set params) {
this.params = params;
}
@@ -72,4 +88,8 @@ public class ApiDiscoveryResponse extends BaseResponse {
public void addParam(ApiParameterResponse param) {
this.params.add(param);
}
+
+ public void addApiResponse(ApiResponseResponse apiResponse) {
+ this.apiResponse.add(apiResponse);
+ }
}
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
index 9138288e102..fa6dc1752d2 100644
--- a/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiParameterResponse.java
+++ b/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiParameterResponse.java
@@ -40,6 +40,9 @@ public class ApiParameterResponse extends BaseResponse {
@SerializedName(ApiConstants.SINCE) @Param(description="version of CloudStack the api was introduced in")
private String since;
+ @SerializedName("related") @Param(description="comma separated related apis to get the parameter")
+ private String related;
+
public ApiParameterResponse(){
}
@@ -67,4 +70,12 @@ public class ApiParameterResponse extends BaseResponse {
this.since = since;
}
+ public String getRelated() {
+ return related;
+ }
+
+ public void setRelated(String related) {
+ this.related = related;
+ }
+
}
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
new file mode 100644
index 00000000000..b96295e1290
--- /dev/null
+++ b/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiResponseResponse.java
@@ -0,0 +1,45 @@
+// 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 ApiResponseResponse extends BaseResponse {
+ @SerializedName(ApiConstants.NAME) @Param(description="the name of the api response field")
+ private String name;
+
+ @SerializedName(ApiConstants.DESCRIPTION) @Param(description="description of the api response field")
+ private String description;
+
+ @SerializedName(ApiConstants.TYPE) @Param(description="response field type")
+ private String type;
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+}
diff --git a/api/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java
similarity index 89%
rename from api/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java
rename to plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java
index 96ea3ee4d34..611493bfc08 100644
--- a/api/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java
+++ b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java
@@ -17,9 +17,10 @@
package org.apache.cloudstack.discovery;
import com.cloud.utils.component.PluggableService;
+import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.response.ListResponse;
public interface ApiDiscoveryService extends PluggableService {
- ListResponse extends BaseResponse> listApis();
+ ListResponse extends BaseResponse> listApis(RoleType roleType, String apiName);
}
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 ea6b206fa44..5f84486ae49 100644
--- a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java
+++ b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java
@@ -16,7 +16,14 @@
// under the License.
package org.apache.cloudstack.discovery;
+import com.cloud.serializer.Param;
+import com.cloud.server.ManagementServer;
import com.cloud.utils.ReflectUtil;
+import com.cloud.utils.StringUtils;
+import com.cloud.utils.component.ComponentLocator;
+import com.cloud.utils.component.PluggableService;
+import com.google.gson.annotations.SerializedName;
+import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.BaseAsyncCmd;
@@ -25,6 +32,7 @@ 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.ApiResponseResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.log4j.Logger;
@@ -32,6 +40,7 @@ import javax.ejb.Local;
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;
@@ -40,30 +49,57 @@ import java.util.Set;
public class ApiDiscoveryServiceImpl implements ApiDiscoveryService {
private static final Logger s_logger = Logger.getLogger(ApiDiscoveryServiceImpl.class);
- private ListResponse _discoveryResponse = new ListResponse();
+ private static Map> _roleTypeDiscoveryResponseListMap;
- private Map> _apiNameCmdClassMap = new HashMap>();
+ private static Map _apiNameDiscoveryResponseMap =
+ new HashMap();
+
+ private static Map> _apiNameRoleTypeListMap = null;
protected ApiDiscoveryServiceImpl() {
super();
- generateApiNameCmdClassMap();
- cacheListApiResponse();
+ if (_roleTypeDiscoveryResponseListMap == null) {
+ long startTime = System.nanoTime();
+ _roleTypeDiscoveryResponseListMap = new HashMap>();
+ for (RoleType roleType: RoleType.values())
+ _roleTypeDiscoveryResponseListMap.put(roleType, new ArrayList());
+ cacheResponseMap();
+ long endTime = System.nanoTime();
+ s_logger.info("Api Discovery Service: Annotation, docstrings, api relation graph processed in " + (endTime - startTime) / 1000000.0 + " ms");
+ }
}
- private void generateApiNameCmdClassMap() {
+ private Map> getApiNameRoleTypeListMap() {
+ Map> apiNameRoleTypeMap = new HashMap>();
+ ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name);
+ List services = locator.getAllPluggableServices();
+ services.add((PluggableService) ComponentLocator.getComponent(ManagementServer.Name));
+ for (PluggableService service : services) {
+ for (Map.Entry entry: service.getProperties().entrySet()) {
+ String apiName = entry.getKey();
+ String roleMask = entry.getValue();
+ try {
+ short cmdPermissions = Short.parseShort(roleMask);
+ if (!apiNameRoleTypeMap.containsKey(apiName))
+ apiNameRoleTypeMap.put(apiName, new ArrayList());
+ for (RoleType roleType: RoleType.values()) {
+ if ((cmdPermissions & roleType.getValue()) != 0)
+ apiNameRoleTypeMap.get(apiName).add(roleType);
+ }
+ } catch (NumberFormatException nfe) {
+ }
+ }
+ }
+ return apiNameRoleTypeMap;
+ }
+
+ private void cacheResponseMap() {
Set> cmdClasses = ReflectUtil.getClassesWithAnnotation(APICommand.class,
new String[]{"org.apache.cloudstack.api", "com.cloud.api"});
- for(Class> cmdClass: cmdClasses)
- _apiNameCmdClassMap.put(cmdClass.getAnnotation(APICommand.class).name(), cmdClass);
- }
+ Map> responseApiNameListMap = new HashMap>();
- private void cacheListApiResponse() {
-
- List apiDiscoveryResponses = new ArrayList();
-
- for(String key: _apiNameCmdClassMap.keySet()) {
- Class> cmdClass = _apiNameCmdClassMap.get(key);
+ for(Class> cmdClass: cmdClasses) {
APICommand apiCmdAnnotation = cmdClass.getAnnotation(APICommand.class);
if (apiCmdAnnotation == null)
apiCmdAnnotation = cmdClass.getSuperclass().getAnnotation(APICommand.class);
@@ -72,10 +108,33 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService {
|| apiCmdAnnotation.name().isEmpty())
continue;
+ String apiName = apiCmdAnnotation.name();
+ String responseName = apiCmdAnnotation.responseObject().getName();
+ if (!responseName.contains("SuccessResponse")) {
+ if (!responseApiNameListMap.containsKey(responseName))
+ responseApiNameListMap.put(responseName, new ArrayList());
+ responseApiNameListMap.get(responseName).add(apiName);
+ }
ApiDiscoveryResponse response = new ApiDiscoveryResponse();
- response.setName(apiCmdAnnotation.name());
+ response.setName(apiName);
response.setDescription(apiCmdAnnotation.description());
- response.setSince(apiCmdAnnotation.since());
+ 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);
+ }
+ }
Field[] fields = ReflectUtil.getAllFieldsForClass(cmdClass,
new Class>[] {BaseCmd.class, BaseAsyncCmd.class, BaseAsyncCreateCmd.class});
@@ -94,26 +153,80 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService {
ApiParameterResponse paramResponse = new ApiParameterResponse();
paramResponse.setName(parameterAnnotation.name());
paramResponse.setDescription(parameterAnnotation.description());
- paramResponse.setType(parameterAnnotation.type().toString());
+ paramResponse.setType(parameterAnnotation.type().toString().toLowerCase());
paramResponse.setLength(parameterAnnotation.length());
paramResponse.setRequired(parameterAnnotation.required());
- paramResponse.setSince(parameterAnnotation.since());
+ if (!parameterAnnotation.since().isEmpty())
+ paramResponse.setSince(parameterAnnotation.since());
+ paramResponse.setRelated(parameterAnnotation.entityType()[0].getName());
response.addParam(paramResponse);
}
}
- response.setObjectName("apis");
- apiDiscoveryResponses.add(response);
+ response.setObjectName("api");
+ _apiNameDiscoveryResponseMap.put(apiName, response);
+ }
+
+ for (String apiName: _apiNameDiscoveryResponseMap.keySet()) {
+ ApiDiscoveryResponse response = _apiNameDiscoveryResponseMap.get(apiName);
+ Set processedParams = new HashSet();
+ for (ApiParameterResponse param: response.getParams()) {
+ if (responseApiNameListMap.containsKey(param.getRelated())) {
+ List relatedApis = responseApiNameListMap.get(param.getRelated());
+ param.setRelated(StringUtils.join(relatedApis, ","));
+ } else {
+ param.setRelated(null);
+ }
+ processedParams.add(param);
+ }
+ response.setParams(processedParams);
+
+ if (responseApiNameListMap.containsKey(response.getRelated())) {
+ List relatedApis = responseApiNameListMap.get(response.getRelated());
+ relatedApis.remove(apiName);
+ response.setRelated(StringUtils.join(relatedApis, ","));
+ } else {
+ response.setRelated(null);
+ }
+ _apiNameDiscoveryResponseMap.put(apiName, response);
}
- _discoveryResponse.setResponses(apiDiscoveryResponses);
}
@Override
- public ListResponse extends BaseResponse> listApis() {
- return _discoveryResponse;
+ public ListResponse extends BaseResponse> listApis(RoleType roleType, String name) {
+ // Creates roles based response list cache the first time listApis is called
+ // Due to how adapters work, this cannot be done when mgmt loads
+ if (_apiNameRoleTypeListMap == null) {
+ long startTime = System.nanoTime();
+ _apiNameRoleTypeListMap = getApiNameRoleTypeListMap();
+ for (Map.Entry> entry: _apiNameRoleTypeListMap.entrySet()) {
+ String apiName = entry.getKey();
+ for (RoleType roleTypeInList: entry.getValue()) {
+ _roleTypeDiscoveryResponseListMap.get(roleTypeInList).add(
+ _apiNameDiscoveryResponseMap.get(apiName));
+ }
+ }
+ long endTime = System.nanoTime();
+ s_logger.info("Api Discovery Service: List apis cached in " + (endTime - startTime) / 1000000.0 + " ms");
+ }
+ ListResponse response = new ListResponse();
+ if (name != null) {
+ if (!_apiNameDiscoveryResponseMap.containsKey(name))
+ return null;
+
+ List singleResponse = new ArrayList();
+ singleResponse.add(_apiNameDiscoveryResponseMap.get(name));
+ response.setResponses(singleResponse);
+
+ } else {
+ response.setResponses(_roleTypeDiscoveryResponseListMap.get(roleType));
+ }
+ return response;
}
@Override
- public String[] getPropertiesFiles() {
- return new String[] { "api-discovery_commands.properties" };
+ public Map getProperties() {
+ Map apiDiscoveryPropertyMap = new HashMap();
+ apiDiscoveryPropertyMap.put("listApis", "15");
+ return apiDiscoveryPropertyMap;
}
}
diff --git a/plugins/hypervisors/simulator/src/com/cloud/server/ManagementServerSimulatorImpl.java b/plugins/hypervisors/simulator/src/com/cloud/server/ManagementServerSimulatorImpl.java
index ad42c23380e..44ab26a020a 100644
--- a/plugins/hypervisors/simulator/src/com/cloud/server/ManagementServerSimulatorImpl.java
+++ b/plugins/hypervisors/simulator/src/com/cloud/server/ManagementServerSimulatorImpl.java
@@ -17,16 +17,16 @@
package com.cloud.server;
+import com.cloud.utils.PropertiesUtil;
+
+import java.util.Map;
+
public class ManagementServerSimulatorImpl extends ManagementServerExtImpl {
@Override
- public String[] getPropertiesFiles() {
- String[] apis = super.getPropertiesFiles();
- String[] newapis = new String[apis.length + 1];
- for (int i = 0; i < apis.length; i++) {
- newapis[i] = apis[i];
- }
-
- newapis[apis.length] = "commands-simulator.properties";
- return newapis;
+ public Map getProperties() {
+ Map apiNameRoleMaskMapping = super.getProperties();
+ apiNameRoleMaskMapping.putAll(PropertiesUtil.processConfigFile(new String[]
+ {"commands-simulator.properties"}));
+ return apiNameRoleMaskMapping;
}
}
diff --git a/plugins/hypervisors/vmware/src/com/cloud/network/element/CiscoNexusVSMElement.java b/plugins/hypervisors/vmware/src/com/cloud/network/element/CiscoNexusVSMElement.java
index 911078eac50..2cf87877859 100644
--- a/plugins/hypervisors/vmware/src/com/cloud/network/element/CiscoNexusVSMElement.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/network/element/CiscoNexusVSMElement.java
@@ -17,6 +17,7 @@
package com.cloud.network.element;
+import java.lang.String;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
@@ -24,6 +25,7 @@ import java.util.Set;
import javax.ejb.Local;
+import com.cloud.utils.PropertiesUtil;
import org.apache.log4j.Logger;
import com.cloud.api.commands.DeleteCiscoNexusVSMCmd;
@@ -237,7 +239,8 @@ public class CiscoNexusVSMElement extends CiscoNexusVSMDeviceManagerImpl impleme
}
@Override
- public String[] getPropertiesFiles() {
- return new String[] { "cisconexusvsm_commands.properties" };
+ public Map getProperties() {
+ return PropertiesUtil.processConfigFile(new String[]
+ { "cisconexusvsm_commands.properties" });
}
}
diff --git a/plugins/network-elements/f5/src/com/cloud/network/element/F5ExternalLoadBalancerElement.java b/plugins/network-elements/f5/src/com/cloud/network/element/F5ExternalLoadBalancerElement.java
index 335dc6ecfec..438498ff38c 100644
--- a/plugins/network-elements/f5/src/com/cloud/network/element/F5ExternalLoadBalancerElement.java
+++ b/plugins/network-elements/f5/src/com/cloud/network/element/F5ExternalLoadBalancerElement.java
@@ -16,6 +16,7 @@
// under the License.
package com.cloud.network.element;
+import java.lang.String;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -24,6 +25,7 @@ import java.util.Set;
import javax.ejb.Local;
+import com.cloud.utils.PropertiesUtil;
import org.apache.log4j.Logger;
import com.cloud.api.ApiDBUtils;
@@ -260,8 +262,9 @@ public class F5ExternalLoadBalancerElement extends ExternalLoadBalancerDeviceMan
}
@Override
- public String[] getPropertiesFiles() {
- return new String[] { "f5bigip_commands.properties" };
+ public Map getProperties() {
+ return PropertiesUtil.processConfigFile(new String[]
+ { "f5bigip_commands.properties" });
}
@Override
diff --git a/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java b/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java
index f491e66925a..55722ae23ab 100644
--- a/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java
+++ b/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java
@@ -16,6 +16,7 @@
// under the License.
package com.cloud.network.element;
+import java.lang.String;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -24,6 +25,7 @@ import java.util.Set;
import javax.ejb.Local;
+import com.cloud.utils.PropertiesUtil;
import org.apache.log4j.Logger;
import com.cloud.api.ApiDBUtils;
@@ -402,8 +404,9 @@ public class JuniperSRXExternalFirewallElement extends ExternalFirewallDeviceMan
}
@Override
- public String[] getPropertiesFiles() {
- return new String[] { "junipersrx_commands.properties"};
+ public Map getProperties() {
+ return PropertiesUtil.processConfigFile(new String[]
+ { "junipersrx_commands.properties"});
}
@Override
diff --git a/plugins/network-elements/netscaler/src/com/cloud/network/element/NetscalerElement.java b/plugins/network-elements/netscaler/src/com/cloud/network/element/NetscalerElement.java
index b1fe949632c..ac1619ef1f8 100644
--- a/plugins/network-elements/netscaler/src/com/cloud/network/element/NetscalerElement.java
+++ b/plugins/network-elements/netscaler/src/com/cloud/network/element/NetscalerElement.java
@@ -26,6 +26,7 @@ import java.util.Set;
import javax.ejb.Local;
+import com.cloud.utils.PropertiesUtil;
import org.apache.log4j.Logger;
import com.cloud.agent.AgentManager;
@@ -464,8 +465,9 @@ StaticNatServiceProvider {
}
@Override
- public String[] getPropertiesFiles() {
- return new String[] { "netscalerloadbalancer_commands.properties" };
+ public Map getProperties() {
+ return PropertiesUtil.processConfigFile(new String[]
+ { "netscalerloadbalancer_commands.properties" });
}
@Override
diff --git a/plugins/network-elements/nicira-nvp/src/com/cloud/network/element/NiciraNvpElement.java b/plugins/network-elements/nicira-nvp/src/com/cloud/network/element/NiciraNvpElement.java
index cc53ee1afae..22fab500fdb 100644
--- a/plugins/network-elements/nicira-nvp/src/com/cloud/network/element/NiciraNvpElement.java
+++ b/plugins/network-elements/nicira-nvp/src/com/cloud/network/element/NiciraNvpElement.java
@@ -27,6 +27,7 @@ import java.util.UUID;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
+import com.cloud.utils.PropertiesUtil;
import org.apache.log4j.Logger;
import com.cloud.agent.AgentManager;
@@ -540,8 +541,9 @@ public class NiciraNvpElement extends AdapterBase implements
}
@Override
- public String[] getPropertiesFiles() {
- return new String[] { "nicira-nvp_commands.properties" };
+ public Map getProperties() {
+ return PropertiesUtil.processConfigFile(new String[]
+ { "nicira-nvp_commands.properties" });
}
@Override
diff --git a/pom.xml b/pom.xml
index 1dcf36fe7ed..aad124b0baf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -245,6 +245,7 @@
scripts/vm/systemvm/id_rsa.cloud
tools/devcloud/basebuild/puppet-devcloudinitial/files/network.conf
tools/devcloud/devcloud.cfg
+ tools/devcloud-kvm/devcloud-kvm.cfg
ui/lib/flot/jquery.colorhelpers.js
ui/lib/flot/jquery.flot.crosshair.js
ui/lib/flot/jquery.flot.fillbetween.js
@@ -371,6 +372,7 @@
developer
tools/apidoc
tools/devcloud
+ tools/devcloud-kvm
tools/marvin
tools/cli
diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java
index bf21664c07f..b2a6a87d9e5 100755
--- a/server/src/com/cloud/api/ApiServer.java
+++ b/server/src/com/cloud/api/ApiServer.java
@@ -51,9 +51,9 @@ 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.APILimitChecker;
-import org.apache.cloudstack.acl.ControlledEntity;
+import org.apache.cloudstack.acl.APIChecker;
+import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.*;
import org.apache.cloudstack.api.command.user.account.ListAccountsCmd;
import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd;
@@ -149,8 +149,8 @@ public class ApiServer implements HttpRequestHandler {
@Inject(adapter = APILimitChecker.class)
protected Adapters _apiLimitCheckers;
- @Inject(adapter = APIAccessChecker.class)
- protected Adapters _apiAccessCheckers;
+ @Inject(adapter = APIChecker.class)
+ protected Adapters _apiAccessCheckers;
private Account _systemAccount = null;
private User _systemUser = null;
@@ -564,14 +564,14 @@ public class ApiServer implements HttpRequestHandler {
}
}
if (!isCommandAvailable(user, commandName)) {
- s_logger.warn("The given command:" + commandName + " does not exist or it is not available for user");
+ s_logger.debug("The given command:" + commandName + " does not exist or it is not available for user with id:" + userId);
throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command does not exist or it is not available for user");
}
return true;
} else {
// check against every available command to see if the command exists or not
- if (!isCommandAvailable(null, commandName) && !commandName.equals("login") && !commandName.equals("logout")) {
- s_logger.warn("The given command:" + commandName + " does not exist or it is not available for user");
+ if (!doesCommandExist(commandName) && !commandName.equals("login") && !commandName.equals("logout")) {
+ s_logger.debug("The given command:" + commandName + " does not exist or it is not available for user with id:" + userId);
throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command does not exist or it is not available for user");
}
}
@@ -665,8 +665,8 @@ public class ApiServer implements HttpRequestHandler {
UserContext.updateContext(user.getId(), account, null);
if (!isCommandAvailable(user, commandName)) {
- s_logger.warn("The given command:" + commandName + " does not exist or it is not available for user");
- throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command:" + commandName + " does not exist or it is not available for user");
+ s_logger.debug("The given command:" + commandName + " does not exist or it is not available for user");
+ throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command:" + commandName + " does not exist or it is not available for user with id:" + userId);
}
// verify secret key exists
@@ -802,6 +802,7 @@ public class ApiServer implements HttpRequestHandler {
return true;
}
+
private boolean isRequestAllowed(User user) {
Account account = ApiDBUtils.findAccountById(user.getAccountId());
if ( _accountMgr.isRootAdmin(account.getType()) ){
@@ -816,10 +817,25 @@ public class ApiServer implements HttpRequestHandler {
return true;
}
+ private boolean doesCommandExist(String apiName) {
+ for (APIChecker apiChecker : _apiAccessCheckers) {
+ // If any checker has api info on the command, return true
+ if (apiChecker.checkExistence(apiName))
+ return true;
+ }
+ return false;
+ }
+
private boolean isCommandAvailable(User user, String commandName) {
- for (APIAccessChecker apiChecker : _apiAccessCheckers) {
+ if (user == null) {
+ return false;
+ }
+
+ Account account = _accountMgr.getAccount(user.getAccountId());
+ RoleType roleType = _accountMgr.getRoleType(account);
+ for (APIChecker apiChecker : _apiAccessCheckers) {
// Fail the checking if any checker fails to verify
- if (!apiChecker.canAccessAPI(user, commandName))
+ if (!apiChecker.checkAccess(roleType, commandName))
return false;
}
return true;
diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java
index b5b8b1a73cd..823b74f269b 100755
--- a/server/src/com/cloud/network/element/VirtualRouterElement.java
+++ b/server/src/com/cloud/network/element/VirtualRouterElement.java
@@ -24,6 +24,7 @@ import java.util.Set;
import javax.ejb.Local;
+import com.cloud.utils.PropertiesUtil;
import org.apache.cloudstack.api.command.admin.router.ConfigureVirtualRouterElementCmd;
import org.apache.cloudstack.api.command.admin.router.ListVirtualRouterElementsCmd;
import org.apache.log4j.Logger;
@@ -680,8 +681,9 @@ public class VirtualRouterElement extends AdapterBase implements VirtualRouterEl
}
@Override
- public String[] getPropertiesFiles() {
- return new String[] { "virtualrouter_commands.properties" };
+ public Map getProperties() {
+ return PropertiesUtil.processConfigFile(new String[]
+ { "virtualrouter_commands.properties" });
}
@Override
diff --git a/server/src/com/cloud/server/ManagementServerExtImpl.java b/server/src/com/cloud/server/ManagementServerExtImpl.java
index b7320276341..8a59d2f9c1c 100644
--- a/server/src/com/cloud/server/ManagementServerExtImpl.java
+++ b/server/src/com/cloud/server/ManagementServerExtImpl.java
@@ -29,6 +29,7 @@ import com.cloud.domain.dao.DomainDao;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.projects.Project;
+import com.cloud.utils.PropertiesUtil;
import org.apache.cloudstack.api.response.UsageTypeResponse;
import com.cloud.usage.UsageJobVO;
import com.cloud.usage.UsageTypes;
@@ -206,8 +207,9 @@ public class ManagementServerExtImpl extends ManagementServerImpl implements Man
}
@Override
- public String[] getPropertiesFiles() {
- return new String[] { "commands.properties", "commands-ext.properties" };
+ public Map getProperties() {
+ return PropertiesUtil.processConfigFile(new String[]
+ { "commands.properties", "commands-ext.properties" });
}
private Date computeAdjustedTime(Date initialDate, TimeZone targetTZ, boolean adjustToDayStart) {
diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java
index dcecaf40a57..79ad759dd0b 100755
--- a/server/src/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/com/cloud/server/ManagementServerImpl.java
@@ -223,6 +223,7 @@ import com.cloud.utils.EnumUtils;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.PasswordGenerator;
+import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.Ternary;
import com.cloud.utils.component.Adapters;
import com.cloud.utils.component.ComponentLocator;
@@ -2297,8 +2298,9 @@ public class ManagementServerImpl implements ManagementServer {
}
@Override
- public String[] getPropertiesFiles() {
- return new String[] { "commands.properties" };
+ public Map getProperties() {
+ return PropertiesUtil.processConfigFile(new String[]
+ { "commands.properties" });
}
protected class EventPurgeTask implements Runnable {
diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java
index c6a7d51f08d..b910a03f99b 100755
--- a/server/src/com/cloud/user/AccountManagerImpl.java
+++ b/server/src/com/cloud/user/AccountManagerImpl.java
@@ -37,6 +37,7 @@ import javax.ejb.Local;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.acl.ControlledEntity;
+import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
import org.apache.cloudstack.api.command.admin.user.RegisterCmd;
@@ -1542,6 +1543,31 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag
}
}
+ @Override
+ public RoleType getRoleType(Account account) {
+ RoleType roleType = RoleType.Unknown;
+ if (account == null)
+ return roleType;
+ short accountType = account.getType();
+
+ // Account type to role type translation
+ switch (accountType) {
+ case Account.ACCOUNT_TYPE_ADMIN:
+ roleType = RoleType.Admin;
+ break;
+ case Account.ACCOUNT_TYPE_DOMAIN_ADMIN:
+ roleType = RoleType.DomainAdmin;
+ break;
+ case Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN:
+ roleType = RoleType.ResourceAdmin;
+ break;
+ case Account.ACCOUNT_TYPE_NORMAL:
+ roleType = RoleType.User;
+ break;
+ }
+ return roleType;
+ }
+
@Override
public User getActiveUser(long userId) {
return _userDao.findById(userId);
diff --git a/server/test/com/cloud/user/MockAccountManagerImpl.java b/server/test/com/cloud/user/MockAccountManagerImpl.java
index ae5d0e5de4b..550304adfff 100644
--- a/server/test/com/cloud/user/MockAccountManagerImpl.java
+++ b/server/test/com/cloud/user/MockAccountManagerImpl.java
@@ -23,6 +23,7 @@ import javax.ejb.Local;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.acl.ControlledEntity;
+import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import com.cloud.api.query.vo.ControlledViewEntity;
@@ -344,4 +345,9 @@ public class MockAccountManagerImpl implements Manager, AccountManager, AccountS
return null;
}
+ @Override
+ public RoleType getRoleType(Account account) {
+ return null;
+ }
+
}
diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py
index 7739aea633f..0b281a29c1d 100644
--- a/tools/apidoc/gen_toc.py
+++ b/tools/apidoc/gen_toc.py
@@ -129,7 +129,6 @@ known_categories = {
'AutoScale': 'AutoScale',
'Counter': 'AutoScale',
'Condition': 'AutoScale',
- 'Api': 'API Discovery',
}
diff --git a/tools/apidoc/pom.xml b/tools/apidoc/pom.xml
index e0b02bc5dc6..bc7411f7013 100644
--- a/tools/apidoc/pom.xml
+++ b/tools/apidoc/pom.xml
@@ -57,7 +57,7 @@
${client.config.jars}
./target
-f
- ${client.config.conf}/commands.properties,${client.config.conf}/commands-ext.properties,${client.config.conf}/virtualrouter_commands.properties,${client.config.conf}/nicira-nvp_commands.properties,${client.config.conf}/api-discovery_commands.properties
+ ${client.config.conf}/commands.properties,${client.config.conf}/commands-ext.properties,${client.config.conf}/virtualrouter_commands.properties,${client.config.conf}/nicira-nvp_commands.properties
diff --git a/tools/devcloud-kvm/README.md b/tools/devcloud-kvm/README.md
new file mode 100644
index 00000000000..3261fbe4b8e
--- /dev/null
+++ b/tools/devcloud-kvm/README.md
@@ -0,0 +1,21 @@
+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.
+
+===========================================================
+
+This directory hosts configs for setting up the devcloud-kvm
+environment.
diff --git a/tools/devcloud-kvm/devcloud-kvm.cfg b/tools/devcloud-kvm/devcloud-kvm.cfg
new file mode 100644
index 00000000000..47a128fea14
--- /dev/null
+++ b/tools/devcloud-kvm/devcloud-kvm.cfg
@@ -0,0 +1,97 @@
+{
+ "zones": [
+ {
+ "name": "DevCloudKVM0",
+ "physical_networks": [
+ {
+ "broadcastdomainrange": "Zone",
+ "name": "test-network",
+ "traffictypes": [
+ {
+ "typ": "Guest"
+ },
+ {
+ "typ": "Management"
+ }
+ ],
+ "providers": [
+ {
+ "broadcastdomainrange": "ZONE",
+ "name": "VirtualRouter"
+ },
+ {
+ "broadcastdomainrange": "Pod",
+ "name": "SecurityGroupProvider"
+ }
+ ]
+ }
+ ],
+ "dns2": "4.4.4.4",
+ "dns1": "8.8.8.8",
+ "securitygroupenabled": "true",
+ "localstorageenabled": "true",
+ "networktype": "Basic",
+ "pods": [
+ {
+ "endip": "192.168.100.250",
+ "name": "test00",
+ "startip": "192.168.100.200",
+ "guestIpRanges": [
+ {
+ "startip": "192.168.100.100",
+ "endip": "192.168.100.199",
+ "netmask": "255.255.255.0",
+ "gateway": "192.168.100.1"
+ }
+ ],
+ "netmask": "255.255.255.0",
+ "clusters": [
+ {
+ "clustername": "test000",
+ "hypervisor": "KVM",
+ "hosts": [
+ {
+ "username": "root",
+ "url": "http://192.168.100.10/",
+ "password": "password"
+ }
+ ],
+ "clustertype": "CloudManaged"
+ }
+ ],
+ "gateway": "192.168.100.1"
+ }
+ ],
+ "internaldns1": "192.168.100.10",
+ "secondaryStorages": [
+ {
+ "url": "nfs://192.168.100.10:/nfs/secondary"
+ }
+ ]
+ }
+ ],
+ "logger": [
+ {
+ "name": "TestClient",
+ "file": "/tmp/testclient.log"
+ },
+ {
+ "name": "TestCase",
+ "file": "/tmp/testcase.log"
+ }
+ ],
+ "mgtSvr": [
+ {
+ "mgtSvrIp": "127.0.0.1",
+ "port": 8096
+ }
+ ],
+ "dbSvr":
+ {
+ "dbSvr": "127.0.0.1",
+ "port": 3306,
+ "user": "cloud",
+ "passwd": "cloud",
+ "db": "cloud"
+ }
+}
diff --git a/tools/devcloud-kvm/devcloud-kvm.sql b/tools/devcloud-kvm/devcloud-kvm.sql
new file mode 100644
index 00000000000..97478834bf3
--- /dev/null
+++ b/tools/devcloud-kvm/devcloud-kvm.sql
@@ -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.
+
+
+INSERT INTO `cloud`.`disk_offering` (id, name, uuid, display_text, created, use_local_storage, type, disk_size) VALUES (17, 'tinyOffering', UUID(), 'tinyOffering', NOW(), 1, 'Service', 0);
+INSERT INTO `cloud`.`service_offering` (id, cpu, speed, ram_size) VALUES (17, 1, 100, 100);
+INSERT INTO `cloud`.`disk_offering` (id, name, uuid, display_text, created, type, disk_size) VALUES (18, 'tinyDiskOffering', UUID(), 'tinyDiskOffering', NOW(), 'Disk', 1073741824);
+INSERT INTO `cloud`.`configuration` (instance, name,value) VALUE('DEFAULT','router.ram.size', '100');
+INSERT INTO `cloud`.`configuration` (instance, name,value) VALUE('DEFAULT','router.cpu.mhz','100');
+INSERT INTO `cloud`.`configuration` (instance, name,value) VALUE('DEFAULT','console.ram.size','100');
+INSERT INTO `cloud`.`configuration` (instance, name,value) VALUE('DEFAULT','console.cpu.mhz', '100');
+INSERT INTO `cloud`.`configuration` (instance, name,value) VALUE('DEFAULT','ssvm.ram.size','100');
+INSERT INTO `cloud`.`configuration` (instance, name,value) VALUE('DEFAULT','ssvm.cpu.mhz','100');
+INSERT INTO `cloud`.`configuration` (instance, name, value) VALUE('DEFAULT', 'system.vm.use.local.storage', 'true');
+INSERT INTO `cloud`.`configuration` (instance, name, value) VALUE('DEFAULT', 'expunge.workers', '3');
+INSERT INTO `cloud`.`configuration` (instance, name, value) VALUE('DEFAULT', 'expunge.delay', '60');
+INSERT INTO `cloud`.`configuration` (instance, name, value) VALUE('DEFAULT', 'expunge.interval', '60');
+INSERT INTO `cloud`.`configuration` (instance, name, value) VALUE('DEFAULT', 'enable.ec2.api', 'true');
+INSERT INTO `cloud`.`configuration` (instance, name, value) VALUE('DEFAULT', 'enable.s3.api', 'true');
+INSERT INTO `cloud`.`configuration` (instance, name, value) VALUE('DEFAULT', 'host', '192.168.100.10');
+INSERT INTO `cloud`.`configuration` (instance, name, value) VALUE('DEFAULT', 'management.network.cidr', '192.168.100.0/24');
+INSERT INTO `cloud`.`configuration` (instance, name, value) VALUE('DEFAULT', 'secstorage.allowed.internal.sites', '192.168.0.0/8');
+UPDATE `cloud`.`configuration` SET value='10' where name = 'storage.overprovisioning.factor';
+UPDATE `cloud`.`configuration` SET value='10' where name = 'cpu.overprovisioning.factor';
+UPDATE `cloud`.`configuration` SET value='10' where name = 'mem.overprovisioning.factor';
+UPDATE `cloud`.`vm_template` SET unique_name="tiny Linux",name="tiny Linux",url="http://marcus.mlsorensen.com/cloudstack-extras/ttylinux_pv.qcow2",checksum="81dcf4b4ca05a3b637a040e851568f29",display_text="tiny Linux",format='QCOW2',hypervisor_type='KVM' where id=5;
diff --git a/tools/devcloud-kvm/pom.xml b/tools/devcloud-kvm/pom.xml
new file mode 100644
index 00000000000..c9af192bee3
--- /dev/null
+++ b/tools/devcloud-kvm/pom.xml
@@ -0,0 +1,138 @@
+
+
+ 4.0.0
+ cloud-devcloud-kvm
+ Apache CloudStack Developer Tools
+ pom
+
+ org.apache.cloudstack
+ cloudstack
+ 4.1.0-SNAPSHOT
+ ../../pom.xml
+
+
+
+ mysql
+ mysql-connector-java
+ 5.1.21
+ runtime
+
+
+
+
+ install
+
+
+
+ deploydb
+
+
+ deploydb
+
+
+
+
+
+ org.codehaus.mojo
+ properties-maven-plugin
+ 1.0-alpha-2
+
+
+ initialize
+
+ read-project-properties
+
+
+
+ ${project.parent.basedir}/utils/conf/db.properties
+ ${project.parent.basedir}/utils/conf/db.properties.override
+
+ true
+
+
+
+
+
+ org.codehaus.mojo
+ sql-maven-plugin
+ 1.5
+
+
+
+ mysql
+ mysql-connector-java
+ ${cs.mysql.version}
+
+
+
+ org.gjt.mm.mysql.Driver
+ jdbc:mysql://${db.cloud.host}:${db.cloud.port}/cloud
+ ${db.cloud.username}
+ ${db.cloud.password}
+
+ ${maven.test.skip}
+ true
+
+
+
+ create-schema
+ process-test-resources
+
+ execute
+
+
+
+ ${basedir}/devcloud-kvm.sql
+
+
+
+
+
+
+
+
+
+ deploysvr
+
+
+ deploysvr
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 1.2.1
+
+
+ package
+
+ exec
+
+
+
+
+ python
+
+ ../marvin/marvin/deployDataCenter.py
+ -i
+ devcloud-kvm.cfg
+
+
+
+
+
+
+
+
diff --git a/utils/src/com/cloud/utils/PropertiesUtil.java b/utils/src/com/cloud/utils/PropertiesUtil.java
index 3909ca876b6..90f8af8b33f 100755
--- a/utils/src/com/cloud/utils/PropertiesUtil.java
+++ b/utils/src/com/cloud/utils/PropertiesUtil.java
@@ -17,6 +17,8 @@
package com.cloud.utils;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
@@ -28,6 +30,7 @@ import java.util.Set;
import org.apache.log4j.Logger;
public class PropertiesUtil {
+ private static final Logger s_logger = Logger.getLogger(PropertiesUtil.class);
/**
* Searches the class path and local paths to find the config file.
* @param path path to find. if it starts with / then it's absolute path.
@@ -116,4 +119,41 @@ public class PropertiesUtil {
}
return null;
}
+
+ // Returns key=value pairs by parsing a commands.properties/config file
+ // with syntax; key=cmd;value (with this syntax cmd is stripped) and key=value
+ public static Map processConfigFile(String[] configFiles) {
+ Map configMap = new HashMap();
+ Properties preProcessedCommands = new Properties();
+ for (String configFile : configFiles) {
+ File commandsFile = findConfigFile(configFile);
+ if (commandsFile != null) {
+ try {
+ preProcessedCommands.load(new FileInputStream(commandsFile));
+ } catch (FileNotFoundException fnfex) {
+ // in case of a file within a jar in classpath, try to open stream using url
+ InputStream stream = PropertiesUtil.openStreamFromURL(configFile);
+ if (stream != null) {
+ try {
+ preProcessedCommands.load(stream);
+ } catch (IOException e) {
+ s_logger.error("IO Exception, unable to find properties file:", fnfex);
+ }
+ } else {
+ s_logger.error("Unable to find properites file", fnfex);
+ }
+ } catch (IOException ioe) {
+ s_logger.error("IO Exception loading properties file", ioe);
+ }
+ }
+ }
+
+ for (Object key : preProcessedCommands.keySet()) {
+ String preProcessedCommand = preProcessedCommands.getProperty((String) key);
+ int splitIndex = preProcessedCommand.lastIndexOf(";");
+ String value = preProcessedCommand.substring(splitIndex+1);
+ configMap.put((String)key, value);
+ }
+ return configMap;
+ }
}
diff --git a/utils/src/com/cloud/utils/component/PluggableService.java b/utils/src/com/cloud/utils/component/PluggableService.java
index d2199394a69..f6f72a904d0 100644
--- a/utils/src/com/cloud/utils/component/PluggableService.java
+++ b/utils/src/com/cloud/utils/component/PluggableService.java
@@ -16,9 +16,11 @@
// under the License.
package com.cloud.utils.component;
+import java.util.Map;
+
// This interface defines methods for pluggable code within the Cloud Stack.
public interface PluggableService {
// The config command properties filenames that lists allowed API commands
// and role masks supported by this pluggable service
- String[] getPropertiesFiles();
+ Map getProperties();
}
diff --git a/utils/test/com/cloud/utils/log/CglibThrowableRendererTest.java b/utils/test/com/cloud/utils/log/CglibThrowableRendererTest.java
index 5a9501dcc9c..c1cd81ef08a 100644
--- a/utils/test/com/cloud/utils/log/CglibThrowableRendererTest.java
+++ b/utils/test/com/cloud/utils/log/CglibThrowableRendererTest.java
@@ -18,14 +18,21 @@ package com.cloud.utils.log;
import junit.framework.TestCase;
-import org.apache.log4j.Logger;
+import org.apache.log4j.*;
import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.db.DB;
import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.log4j.spi.RootLogger;
+import org.apache.log4j.spi.ThrowableRenderer;
+
+import java.io.CharArrayWriter;
+import java.io.Writer;
public class CglibThrowableRendererTest extends TestCase {
+ static Logger another = Logger.getLogger("TEST");
+
private final static Logger s_logger = Logger.getLogger(CglibThrowableRendererTest.class);
public static class Test {
@DB
@@ -48,13 +55,40 @@ public class CglibThrowableRendererTest extends TestCase {
}
}
}
+
+ private Logger getAlternateLogger(Writer writer, ThrowableRenderer renderer) {
+ Hierarchy hierarchy = new Hierarchy(new RootLogger(Level.INFO));
+ if (renderer != null) {
+ hierarchy.setThrowableRenderer(renderer);
+ }
+ Logger alternateRoot = hierarchy.getRootLogger();
+ alternateRoot.addAppender(new WriterAppender(new SimpleLayout(), writer));
+ return alternateRoot;
+ }
public void testException() {
+ Writer w = new CharArrayWriter();
+ Logger alt = getAlternateLogger(w, null);
+
Test test = ComponentLocator.inject(Test.class);
try {
test.exception();
} catch (Exception e) {
- s_logger.warn("exception caught", e);
+ alt.warn("exception caught", e);
}
+ // first check that we actually have some call traces containing ""
+ assertTrue(w.toString().contains(""));
+
+ w = new CharArrayWriter();
+ alt = getAlternateLogger(w, new CglibThrowableRenderer());
+
+ try {
+ test.exception();
+ } catch (Exception e) {
+ alt.warn("exception caught", e);
+ }
+ // then we check that CglibThrowableRenderer indeed remove those occurrences
+ assertFalse(w.toString().contains(""));
+
}
}