From 7c5c3c5dfcc985dfd6dd890c96fa840a2bac3e0e Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 9 Jan 2013 05:43:41 -0800 Subject: [PATCH 01/73] api: Re-add deleted addProxyObject method on CloudException - This was removed part of the response work - Re-adding as this kind of method definition is available in several other exception classes and is used in a lot of cmd classes and in service layer - TODO: We need to find a way to replace old code and refactor method with the new definition that gets only uuid. - FIXME: Some tables don't have uuid for ex. ClusterVSMMapVO, in this case we should keep the method until we find a way to fix this issue Partially reverting a88ce6bb7f495dddeb954d1fc7826176646b3590 Signed-off-by: Rohit Yadav --- api/src/com/cloud/exception/CloudException.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/api/src/com/cloud/exception/CloudException.java b/api/src/com/cloud/exception/CloudException.java index fd839565253..2ec61420cee 100644 --- a/api/src/com/cloud/exception/CloudException.java +++ b/api/src/com/cloud/exception/CloudException.java @@ -56,6 +56,15 @@ public class CloudException extends Exception { return; } + public void addProxyObject(Object voObj, Long id, String idFieldName) { + // Get the VO object's table name. + String tablename = AnnotationHelper.getTableName(voObj); + if (tablename != null) { + addProxyObject(tablename, id, idFieldName); + } + return; + } + public ArrayList getIdProxyList() { return idList; } From 9924b64830a406d08f53c22f7934fa31fb99de9d Mon Sep 17 00:00:00 2001 From: Min Chen Date: Wed, 9 Jan 2013 10:42:44 -0800 Subject: [PATCH 02/73] commit 3a3cb60e85c0254ebceff55d0b210ca1ff5386a6 Author: Likitha Shetty Date: Wed Jan 9 11:54:25 2013 +0530 CLOUDSTACK-614: ListTemplates API is not returning "Enable SSH Key" attribute for any given template. Update the TemplateResponse by adding 'sshkeyenabled' attribute to it. This attribute is set to the value that the user passes as input for parameter 'sshkeyenabled' while registering the template. Signed-off-by: Min Chen --- .../apache/cloudstack/api/response/TemplateResponse.java | 9 +++++++-- server/src/com/cloud/api/ApiResponseHelper.java | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/response/TemplateResponse.java b/api/src/org/apache/cloudstack/api/response/TemplateResponse.java index f6f74dac5b7..033c2e243d5 100644 --- a/api/src/org/apache/cloudstack/api/response/TemplateResponse.java +++ b/api/src/org/apache/cloudstack/api/response/TemplateResponse.java @@ -135,8 +135,8 @@ public class TemplateResponse extends BaseResponse implements ControlledEntityRe @SerializedName(ApiConstants.TAGS) @Param(description="the list of resource tags associated with tempate", responseObject = ResourceTagResponse.class) private List tags; - - + @SerializedName(ApiConstants.SSHKEY_ENABLED) @Param(description="true if template is sshkey enabled, false otherwise") + private Boolean sshKeyEnabled; @Override public String getObjectId() { @@ -290,4 +290,9 @@ public class TemplateResponse extends BaseResponse implements ControlledEntityRe public void setTags(List tags) { this.tags = tags; } + + public void setSshKeyEnabled(boolean sshKeyEnabled) { + this.sshKeyEnabled = sshKeyEnabled; + } + } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 37be83efc5c..47754395e7a 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -1265,6 +1265,7 @@ public class ApiResponseHelper implements ResponseGenerator { templateResponse.setFeatured(template.isFeatured()); templateResponse.setExtractable(template.isExtractable() && !(template.getTemplateType() == TemplateType.SYSTEM)); templateResponse.setPasswordEnabled(template.getEnablePassword()); + templateResponse.setSshKeyEnabled(template.getEnableSshKey()); templateResponse.setCrossZones(template.isCrossZones()); templateResponse.setFormat(template.getFormat()); templateResponse.setDetails(template.getDetails()); @@ -1346,6 +1347,7 @@ public class ApiResponseHelper implements ResponseGenerator { templateResponse.setFeatured(template.isFeatured()); templateResponse.setExtractable(template.isExtractable() && !(template.getTemplateType() == TemplateType.SYSTEM)); templateResponse.setPasswordEnabled(template.getEnablePassword()); + templateResponse.setSshKeyEnabled(template.getEnableSshKey()); templateResponse.setCrossZones(template.isCrossZones()); templateResponse.setFormat(template.getFormat()); if (template.getTemplateType() != null) { From 066edc11052ce5527e7624040a8176256959d151 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 9 Jan 2013 11:21:30 -0800 Subject: [PATCH 03/73] plugins: Fix pluggable service getPropertiesFiles() on f5, srx and netscaler Signed-off-by: Rohit Yadav --- .../cloud/network/element/F5ExternalLoadBalancerElement.java | 4 ++-- .../network/element/JuniperSRXExternalFirewallElement.java | 4 ++-- .../src/com/cloud/network/element/NetscalerElement.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) 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 735814c0fbc..335dc6ecfec 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 @@ -260,8 +260,8 @@ public class F5ExternalLoadBalancerElement extends ExternalLoadBalancerDeviceMan } @Override - public String getPropertiesFile() { - return "f5bigip_commands.properties"; + public String[] getPropertiesFiles() { + return 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 0479648c099..f491e66925a 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 @@ -402,8 +402,8 @@ public class JuniperSRXExternalFirewallElement extends ExternalFirewallDeviceMan } @Override - public String getPropertiesFile() { - return "junipersrx_commands.properties"; + public String[] getPropertiesFiles() { + return 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 be9fc6fddd7..b1fe949632c 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 @@ -464,8 +464,8 @@ StaticNatServiceProvider { } @Override - public String getPropertiesFile() { - return "netscalerloadbalancer_commands.properties"; + public String[] getPropertiesFiles() { + return new String[] { "netscalerloadbalancer_commands.properties" }; } @Override From 4e71a5a7b9b6778a59ffb741249b01be4db7c2ad Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 9 Jan 2013 11:22:35 -0800 Subject: [PATCH 04/73] netapp: Fix String conversion method of long id Signed-off-by: Rohit Yadav --- .../netapp/src/com/cloud/netapp/NetappManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/file-systems/netapp/src/com/cloud/netapp/NetappManagerImpl.java b/plugins/file-systems/netapp/src/com/cloud/netapp/NetappManagerImpl.java index 7dcb9d3d032..1fdd2502694 100644 --- a/plugins/file-systems/netapp/src/com/cloud/netapp/NetappManagerImpl.java +++ b/plugins/file-systems/netapp/src/com/cloud/netapp/NetappManagerImpl.java @@ -660,7 +660,7 @@ public class NetappManagerImpl implements NetappManager lun = _lunDao.persist(lun); //Lun id created: 6 digits right justified eg. 000045 - String lunIdStr = lun.getId().toString(); + String lunIdStr = String.valueOf(lun.getId()); String zeroStr = "000000"; int length = lunIdStr.length(); int offset = 6-length; From 999ecb67dfa69a40085279af1d2e19cf5a5f7509 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 9 Jan 2013 11:23:24 -0800 Subject: [PATCH 05/73] plugins: Import and @Parameter fixes Signed-off-by: Rohit Yadav --- .../com/cloud/api/commands/ListExternalLoadBalancersCmd.java | 3 ++- .../com/cloud/api/commands/ListF5LoadBalancerNetworksCmd.java | 1 + .../api/commands/ListNetscalerLoadBalancerNetworksCmd.java | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/network-elements/f5/src/com/cloud/api/commands/ListExternalLoadBalancersCmd.java b/plugins/network-elements/f5/src/com/cloud/api/commands/ListExternalLoadBalancersCmd.java index 3ee8d4865a5..72313aa0c0c 100644 --- a/plugins/network-elements/f5/src/com/cloud/api/commands/ListExternalLoadBalancersCmd.java +++ b/plugins/network-elements/f5/src/com/cloud/api/commands/ListExternalLoadBalancersCmd.java @@ -26,6 +26,7 @@ import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ZoneResponse; import com.cloud.host.Host; import com.cloud.network.element.F5ExternalLoadBalancerElementService; import org.apache.cloudstack.api.response.ExternalLoadBalancerResponse; @@ -40,7 +41,7 @@ public class ListExternalLoadBalancersCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneRespones.class, + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneResponse.class, description="zone Id") private long zoneId; diff --git a/plugins/network-elements/f5/src/com/cloud/api/commands/ListF5LoadBalancerNetworksCmd.java b/plugins/network-elements/f5/src/com/cloud/api/commands/ListF5LoadBalancerNetworksCmd.java index 4ec98f02d58..bf1164b4d05 100644 --- a/plugins/network-elements/f5/src/com/cloud/api/commands/ListF5LoadBalancerNetworksCmd.java +++ b/plugins/network-elements/f5/src/com/cloud/api/commands/ListF5LoadBalancerNetworksCmd.java @@ -31,6 +31,7 @@ import org.apache.cloudstack.api.PlugService; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.NetworkResponse; +import com.cloud.api.response.F5LoadBalancerResponse; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; diff --git a/plugins/network-elements/netscaler/src/com/cloud/api/commands/ListNetscalerLoadBalancerNetworksCmd.java b/plugins/network-elements/netscaler/src/com/cloud/api/commands/ListNetscalerLoadBalancerNetworksCmd.java index b5935f34fda..52476df8316 100644 --- a/plugins/network-elements/netscaler/src/com/cloud/api/commands/ListNetscalerLoadBalancerNetworksCmd.java +++ b/plugins/network-elements/netscaler/src/com/cloud/api/commands/ListNetscalerLoadBalancerNetworksCmd.java @@ -49,7 +49,7 @@ public class ListNetscalerLoadBalancerNetworksCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name=ApiConstants.LOAD_BALANCER_DEVICE_ID, type=CommandType.UUID, entityType = NetscalerLoadBalancerResponse.class, , + @Parameter(name=ApiConstants.LOAD_BALANCER_DEVICE_ID, type=CommandType.UUID, entityType = NetscalerLoadBalancerResponse.class, required = true, description="netscaler load balancer device ID") private Long lbDeviceId; From 09b68ce13fa85702417fbce090379e3e94fecc94 Mon Sep 17 00:00:00 2001 From: Sebastien Goasguen Date: Mon, 17 Dec 2012 16:34:25 +0100 Subject: [PATCH 06/73] Improvements to AWS installation, configuration and use in installation guide --- docs/en-US/aws-api-examples.xml | 145 ++++++++++++++++++ docs/en-US/aws-ec2-configuration.xml | 104 ++++++++++--- docs/en-US/aws-ec2-introduction.xml | 13 +- docs/en-US/aws-ec2-requirements.xml | 9 +- docs/en-US/aws-ec2-supported-commands.xml | 2 +- docs/en-US/aws-ec2-timeouts.xml | 5 +- docs/en-US/aws-ec2-user-setup.xml | 108 +++++++------ docs/en-US/aws-interface-compatibility.xml | 3 +- .../images/compute-service-offerings.png | Bin 0 -> 75482 bytes docs/en-US/images/ec2-s3-configuration.png | 0 10 files changed, 306 insertions(+), 83 deletions(-) create mode 100644 docs/en-US/aws-api-examples.xml create mode 100644 docs/en-US/images/compute-service-offerings.png create mode 100644 docs/en-US/images/ec2-s3-configuration.png diff --git a/docs/en-US/aws-api-examples.xml b/docs/en-US/aws-api-examples.xml new file mode 100644 index 00000000000..ee3b44a5bde --- /dev/null +++ b/docs/en-US/aws-api-examples.xml @@ -0,0 +1,145 @@ + + +%BOOK_ENTITIES; +]> + + + +
+ Examples + There are many tools available to interface with a AWS compatible API. In this section we provide + a few examples that users of &PRODUCT; can build upon. + +
+ Boto Examples + Boto is one of them. It is a Python package available at https://github.com/boto/boto. + In this section we provide two examples of Python scripts that use Boto and have been tested with the + &PRODUCT; AWS API Interface. + First is an EC2 example. Replace the Access and Secret Keys with your own and + update the endpoint. + + + An EC2 Boto example + #!/usr/bin/env python + +import sys +import os +import boto +import boto.ec2 + +region = boto.ec2.regioninfo.RegionInfo(name="ROOT",endpoint="localhost") +apikey='GwNnpUPrO6KgIdZu01z_ZhhZnKjtSdRwuYd4DvpzvFpyxGMvrzno2q05MB0ViBoFYtdqKd' +secretkey='t4eXLEYWw7chBhDlaKf38adCMSHx_wlds6JfSx3z9fSpSOm0AbP9Moj0oGIzy2LSC8iw' + +def main(): + '''Establish connection to EC2 cloud''' + conn =boto.connect_ec2(aws_access_key_id=apikey, + aws_secret_access_key=secretkey, + is_secure=False, + region=region, + port=7080, + path="/awsapi", + api_version="2010-11-15") + + '''Get list of images that I own''' + images = conn.get_all_images() + print images + myimage = images[0] + '''Pick an instance type''' + vm_type='m1.small' + reservation = myimage.run(instance_type=vm_type,security_groups=['default']) + +if __name__ == '__main__': + main() + + + + Second is an S3 example. Replace the Access and Secret keys with your own, + as well as the endpoint of the service. Be sure to also update the file paths to something + that exists on your machine. + + + An S3 Boto Example + #!/usr/bin/env python + +import sys +import os +from boto.s3.key import Key +from boto.s3.connection import S3Connection +from boto.s3.connection import OrdinaryCallingFormat + +apikey='ChOw-pwdcCFy6fpeyv6kUaR0NnhzmG3tE7HLN2z3OB_s-ogF5HjZtN4rnzKnq2UjtnHeg_yLA5gOw' +secretkey='IMY8R7CJQiSGFk4cHwfXXN3DUFXz07cCiU80eM3MCmfLs7kusgyOfm0g9qzXRXhoAPCH-IRxXc3w' + +cf=OrdinaryCallingFormat() + +def main(): + '''Establish connection to S3 service''' + conn =S3Connection(aws_access_key_id=apikey,aws_secret_access_key=secretkey, \ + is_secure=False, \ + host='localhost', \ + port=7080, \ + calling_format=cf, \ + path="/awsapi/rest/AmazonS3") + + try: + bucket=conn.create_bucket('cloudstack') + k = Key(bucket) + k.key = 'test' + try: + k.set_contents_from_filename('/Users/runseb/Desktop/s3cs.py') + except: + print 'could not write file' + pass + except: + bucket = conn.get_bucket('cloudstack') + k = Key(bucket) + k.key = 'test' + try: + k.get_contents_to_filename('/Users/runseb/Desktop/foobar') + except: + print 'Could not get file' + pass + + try: + bucket1=conn.create_bucket('teststring') + k=Key(bucket1) + k.key('foobar') + k.set_contents_from_string('This is my silly test') + except: + bucket1=conn.get_bucket('teststring') + k = Key(bucket1) + k.key='foobar' + k.get_contents_as_string() + +if __name__ == '__main__': + main() + + + + +
+ +
+ JClouds Examples + +
+ +
diff --git a/docs/en-US/aws-ec2-configuration.xml b/docs/en-US/aws-ec2-configuration.xml index d6c4066d1d8..7d26027ba35 100644 --- a/docs/en-US/aws-ec2-configuration.xml +++ b/docs/en-US/aws-ec2-configuration.xml @@ -23,26 +23,88 @@ -->
- Enabling the AWS API Compatible Interface - - The software that provides AWS API compatibility is installed along with &PRODUCT;. However, you must enable the feature and perform some setup steps. - - - Set the global configuration parameter enable.ec2.api to true. See . - Create a set of &PRODUCT; service offerings with names that match the Amazon service offerings. - You can do this through the &PRODUCT; UI as described in the Administration Guide. - Be sure you have included the Amazon default service offering, m1.small. - If you did not already do so when you set the configuration parameter in step 1, restart the Management Server. - # service cloud-management restart - (Optional) The AWS API listens for requests on port 7080. If you prefer AWS API to listen on another port, you can change it as follows: - - Edit the files /etc/cloud/management/server.xml, /etc/cloud/management/server-nonssl.xml, and /etc/cloud/management/server-ssl.xml. - In each file, find the tag <Service name="Catalina7080">. Under this tag, locate <Connector executor="tomcatThreadPool-internal" port= ....<. - Change the port to whatever port you want to use, then save the files. - Restart the Management Server. - If you re-install CloudStack, you will have to make these changes again. + Enabling the EC2 and S3 Compatible Interface + + The software that provides AWS API compatibility is installed along with &PRODUCT;. You must enable the services and perform some setup steps prior to using it. + + + Set the global configuration parameters for each service to true. + See . + Create a set of &PRODUCT; service offerings with names that match the Amazon service offerings. + You can do this through the &PRODUCT; UI as described in the Administration Guide. + Be sure you have included the Amazon default service offering, m1.small. As well as any EC2 instance types that you will use. - - - + If you did not already do so when you set the configuration parameter in step 1, + restart the Management Server. + # service cloud-management restart + + + The following sections provides details to perform these steps + +
+ Enabling the Services + To enable the EC2 and S3 compatible services you need to set the configuration variables enable.ec2.api + and enable.s3.api to true. You do not have to enable both at the same time. Enable the ones you need. + This can be done via the &PRODUCT; GUI by going in Global Settings or via the API. + The snapshot below shows you how to use the GUI to enable these services + + + + + + + + Use the GUI to set the configuration variable to true + + + + + Using the &PRODUCT; API, the easiest is to use the so-called integration port on which you can make + unauthenticated calls. In Global Settings set the port to 8096 and subsequently call the updateConfiguration method. + The following urls shows you how: + + + + http://localhost:8096/client/api?command=updateConfiguration&name=enable.ec2.api&value=true + http://localhost:8096/client/api?command=updateConfiguration&name=enable.ec2.api&value=true + + + + Once you have enabled the services, restart the server. +
+ +
+ Creating EC2 Compatible Service Offerings + You will also need to define compute service offerings with names compatible with the + Amazon EC2 instance types API names (e.g m1.small,m1.large). This can be done via the &PRODUCT; GUI. + Go under Service Offerings select Compute offering and either create + a new compute offering or modify an existing one, ensuring that the name matches an EC2 instance type API name. The snapshot below shows you how: + + + + + + + Use the GUI to set the name of a compute service offering to an EC2 instance + type API name. + + + +
+
+ Modifying the AWS API Port + + (Optional) The AWS API listens for requests on port 7080. If you prefer AWS API to listen on another port, you can change it as follows: + + Edit the files /etc/cloud/management/server.xml, /etc/cloud/management/server-nonssl.xml, + and /etc/cloud/management/server-ssl.xml. + In each file, find the tag <Service name="Catalina7080">. Under this tag, + locate <Connector executor="tomcatThreadPool-internal" port= ....<. + Change the port to whatever port you want to use, then save the files. + Restart the Management Server. + + If you re-install &PRODUCT;, you will have to re-enable the services and if need be update the port. + +
+
diff --git a/docs/en-US/aws-ec2-introduction.xml b/docs/en-US/aws-ec2-introduction.xml index a4df086d465..538c09d5ad1 100644 --- a/docs/en-US/aws-ec2-introduction.xml +++ b/docs/en-US/aws-ec2-introduction.xml @@ -23,16 +23,19 @@ -->
- Amazon Web Services EC2 Compatible Interface + Amazon Web Services Compatible Interface &PRODUCT; can translate Amazon Web Services (AWS) API calls to native &PRODUCT; API calls so that users can continue using existing AWS-compatible tools. This translation service runs as a separate web application in the same tomcat server as the management server of &PRODUCT;, - listening on the same port. This Amazon EC2-compatible API is accessible through a SOAP web - service. + listening on a different port. The Amazon Web Services (AWS) compatible interface provides the + EC2 SOAP and Query APIs as well as the S3 REST API. This service was previously enabled by separate software called CloudBridge. It is now fully integrated with the &PRODUCT; management server. + + The compatible interface for the EC2 Query API and the S3 API are Work In Progress. The S3 compatible API offers a way to store data on the management server file system, it is not an implementation of the S3 backend. + Limitations @@ -42,7 +45,9 @@ Available in fresh installations of &PRODUCT;. Not available through upgrade of previous versions. - If you need to support features such as elastic IP, set up a Citrix NetScaler to provide this service. The commands such as ec2-associate-address will not work without EIP setup. Users running VMs in this zone will be using the NetScaler-enabled network offering (DefaultSharedNetscalerEIP and ELBNetworkOffering). + Features such as Elastic IP (EIP) and Elastic Load Balacing (ELB) are only available in an infrastructure + with a Citrix NetScaler device. Users accessing a Zone with a NetScaler device will need to use a + NetScaler-enabled network offering (DefaultSharedNetscalerEIP and ELBNetworkOffering).
diff --git a/docs/en-US/aws-ec2-requirements.xml b/docs/en-US/aws-ec2-requirements.xml index 59fb5b6f5ab..62e94b1ac9f 100644 --- a/docs/en-US/aws-ec2-requirements.xml +++ b/docs/en-US/aws-ec2-requirements.xml @@ -23,13 +23,14 @@ -->
- System Requirements + Supported API Version - This interface complies with Amazon's WDSL version dated November 15, 2010, available at + The EC2 interface complies with Amazon's WDSL version dated November 15, 2010, available at http://ec2.amazonaws.com/doc/2010-11-15/. - Compatible with the EC2 command-line + The interface is compatible with the EC2 command-line tools EC2 tools v. 1.3.6230, which can be downloaded at http://s3.amazonaws.com/ec2-downloads/ec2-api-tools-1.3-62308.zip. -
\ No newline at end of file + Work is underway to support a more recent version of the EC2 API + diff --git a/docs/en-US/aws-ec2-supported-commands.xml b/docs/en-US/aws-ec2-supported-commands.xml index 9494218cd1c..7cdbcad8095 100644 --- a/docs/en-US/aws-ec2-supported-commands.xml +++ b/docs/en-US/aws-ec2-supported-commands.xml @@ -24,7 +24,7 @@
Supported AWS API Calls - The following Amazon EC2 commands are supported by &PRODUCT; when the AWS API compatibility feature is enabled. + The following Amazon EC2 commands are supported by &PRODUCT; when the AWS API compatible interface is enabled. For a few commands, there are differences between the &PRODUCT; and Amazon EC2 versions, and these differences are noted. The underlying SOAP call for each command is also given, for those who have built tools using those calls. diff --git a/docs/en-US/aws-ec2-timeouts.xml b/docs/en-US/aws-ec2-timeouts.xml index c8b3ec6465f..73d0c16c4df 100644 --- a/docs/en-US/aws-ec2-timeouts.xml +++ b/docs/en-US/aws-ec2-timeouts.xml @@ -24,7 +24,7 @@
Using Timeouts to Ensure AWS API Command Completion - The Amazon EC2 command-line tools have a default connection timeout. When used with &PRODUCT;, a longer timeout might be needed for some commands. If you find that commands are not completing due to timeouts, you can gain more time for commands to finish by overriding the default timeouts on individual commands. You can add the following optional command-line parameters to any &PRODUCT;-supported EC2 command: + The Amazon EC2 command-line tools have a default connection timeout. When used with &PRODUCT;, a longer timeout might be needed for some commands. If you find that commands are not completing due to timeouts, you can specify a custom timeouts. You can add the following optional command-line parameters to any &PRODUCT;-supported EC2 command: @@ -47,4 +47,5 @@ Example: ec2-run-instances 2 –z us-test1 –n 1-3 --connection-timeout 120 --request-timeout 120 -
\ No newline at end of file + The timeouts optional arguments are not specific to &PRODUCT;. + diff --git a/docs/en-US/aws-ec2-user-setup.xml b/docs/en-US/aws-ec2-user-setup.xml index 8607378d88c..edc371ef376 100644 --- a/docs/en-US/aws-ec2-user-setup.xml +++ b/docs/en-US/aws-ec2-user-setup.xml @@ -22,76 +22,84 @@ under the License. -->
- AWS API User Setup Steps + AWS API User Setup In general, users need not be aware that they are using a translation service provided by &PRODUCT;. - They need only send AWS API calls to &PRODUCT;'s endpoint, and it will translate the calls to the native API. - Users of the Amazon EC2 compatible interface will be able to keep their existing EC2 tools + They only need to send AWS API calls to &PRODUCT;'s endpoint, and it will translate the calls to the native &PRODUCT; API. Users of the Amazon EC2 compatible interface will be able to keep their existing EC2 tools and scripts and use them with their &PRODUCT; deployment, by specifying the endpoint of the management server and using the proper user credentials. In order to do this, each user must perform the following configuration steps: - Generate user credentials and register with the service. + Generate user credentials. - Set up the environment variables for the EC2 command-line tools. + Register with the service. - For SOAP access, use the endpoint http://&PRODUCT;-management-server:7080/awsapi. - The &PRODUCT;-management-server can be specified by a fully-qualified domain name or IP address. + For convenience, set up environment variables for the EC2 SOAP command-line tools.
AWS API User Registration - Each user must perform a one-time registration. The user follows these steps: - - - Obtain the following by looking in the &PRODUCT; UI, using the API, or asking the cloud administrator: - - The &PRODUCT; server's publicly available DNS name or IP address - The user account's API key and Secret key - - - - - Generate a private key and a self-signed X.509 certificate. The user substitutes their own desired storage location for /path/to/… below. - - $ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /path/to/private_key.pem -out /path/to/cert.pem - - - - - Register the mapping from the X.509 certificate to the API/Secret keys. - Download the following script from http://download.cloud.com/releases/3.0.3/cloudstack-aws-api-register and run it. - Substitute the values that were obtained in step 1 in the URL below. - - -$ cloudstack-aws-api-register --apikey=User’s &PRODUCT; API key --secretkey=User’s &PRODUCT; Secret key --cert=/path/to/cert.pem --url=http://&PRODUCT;.server:7080/awsapi - - - + Each user must perform a one-time registration. The user follows these steps: + + + Obtain the following by looking in the &PRODUCT; UI, using the API, or asking the cloud administrator: + + + The &PRODUCT; server's publicly available DNS name or IP address + The user account's Access key and Secret key + + + + Generate a private key and a self-signed X.509 certificate. The user substitutes their own desired storage location for /path/to/… below. + + + $ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /path/to/private_key.pem -out /path/to/cert.pem + + + + Register the user X.509 certificate and Access/Secret keys with the AWS compatible service. + If you have the source code of &PRODUCT; go to the awsapi-setup/setup directory and use the Python script + cloudstack-aws-api-register. If you do not have the source then download the script using the following command. + + + wget -O cloudstack-aws-api-register "https://git-wip-us.apache.org/repos/asf?p=incubator-cloudstack.git;a=blob_plain;f=awsapi-setup/setup/cloudstack-aws-api-register;hb=HEAD" + + + Then execute it, using the parameter values that were obtained in step 1. An example is shown below. + + $ cloudstack-aws-api-register --apikey=User’s &PRODUCT; API key --secretkey=User’s &PRODUCT; Secret key --cert=/path/to/cert.pem --url=http://&PRODUCT;.server:7080/awsapi + + + - A user with an existing AWS certificate could choose to use the same certificate with &PRODUCT;, but the public key would be uploaded to the &PRODUCT; management server database. + A user with an existing AWS certificate could choose to use the same certificate with &PRODUCT;, but note that the certificate would be uploaded to the &PRODUCT; management server database.
- AWS API Command-Line Tools Setup - To use the EC2 command-line tools, the user must perform these steps: - - Be sure you have the right version of EC2 Tools. - The supported version is available at http://s3.amazonaws.com/ec2-downloads/ec2-api-tools-1.3-62308.zip. - - - Set up the environment variables that will direct the tools to the server. As a best practice, you may wish to place these commands in a script that may be sourced before using the AWS API translation feature. - $ export EC2_CERT=/path/to/cert.pem -$ export EC2_PRIVATE_KEY=/path/to/private_key.pem -$ export EC2_URL=http://&PRODUCT;.server:7080/awsapi -$ export EC2_HOME=/path/to/EC2_tools_directory - - + AWS API Command-Line Tools Setup + To use the EC2 command-line tools, the user must perform these steps: + + + Be sure you have the right version of EC2 Tools. + The supported version is available at http://s3.amazonaws.com/ec2-downloads/ec2-api-tools-1.3-62308.zip. + + + + Set up the EC2 environment variables. This can be done every time you use the service or you can set them up in the proper shell profile. Replace the endpoint (i.e EC2_URL) with the proper address of your &PRODUCT; management server and port. In a bash shell do the following. + + + $ export EC2_CERT=/path/to/cert.pem + $ export EC2_PRIVATE_KEY=/path/to/private_key.pem + $ export EC2_URL=http://localhost:7080/awsapi + $ export EC2_HOME=/path/to/EC2_tools_directory + + +
-
\ No newline at end of file + diff --git a/docs/en-US/aws-interface-compatibility.xml b/docs/en-US/aws-interface-compatibility.xml index a03d447b50d..2c85c24b36a 100644 --- a/docs/en-US/aws-interface-compatibility.xml +++ b/docs/en-US/aws-interface-compatibility.xml @@ -23,11 +23,12 @@ --> - Amazon Web Service Interface Compatibility + Amazon Web Services Compatible Interface + diff --git a/docs/en-US/images/compute-service-offerings.png b/docs/en-US/images/compute-service-offerings.png new file mode 100644 index 0000000000000000000000000000000000000000..88eb6f80597471a66031df83f6eb16fd5e701630 GIT binary patch literal 75482 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 literal 0 HcmV?d00001 diff --git a/docs/en-US/images/ec2-s3-configuration.png b/docs/en-US/images/ec2-s3-configuration.png new file mode 100644 index 00000000000..e69de29bb2d From 3dfd81fd6bcca92edab1f9ea814d0850d3202451 Mon Sep 17 00:00:00 2001 From: Koushik Das Date: Tue, 11 Dec 2012 18:25:05 +0530 Subject: [PATCH 07/73] CLOUDSTACK-605: Host physical CPU is incorrectly calculated for Vmware hosts Fixed logic to compute Vmware host cpu Signed-off-by: Koushik Das --- .../src/com/cloud/hypervisor/vmware/mo/HostMO.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java index 35570481d41..a765b42fd78 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java @@ -840,11 +840,9 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { VmwareHypervisorHostResourceSummary summary = new VmwareHypervisorHostResourceSummary(); - HostConnectInfo hostInfo = _context.getService().queryHostConnectionInfo(_mor); - HostHardwareSummary hardwareSummary = hostInfo.getHost().getHardware(); - + HostHardwareSummary hardwareSummary = getHostHardwareSummary(); // TODO: not sure how hyper-thread is counted in VMware resource pool - summary.setCpuCount(hardwareSummary.getNumCpuCores()*hardwareSummary.getNumCpuPkgs()); + summary.setCpuCount(hardwareSummary.getNumCpuCores()); summary.setMemoryBytes(hardwareSummary.getMemorySize()); summary.setCpuSpeed(hardwareSummary.getCpuMhz()); @@ -922,14 +920,13 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { ComputeResourceSummary resourceSummary = new ComputeResourceSummary(); // TODO: not sure how hyper-threading is counted in VMware - short totalCores = (short)(hardwareSummary.getNumCpuCores()*hardwareSummary.getNumCpuPkgs()); - resourceSummary.setNumCpuCores(totalCores); + resourceSummary.setNumCpuCores(hardwareSummary.getNumCpuCores()); // Note: memory here is in Byte unit resourceSummary.setTotalMemory(hardwareSummary.getMemorySize()); - // Total CPU is based on socket x core x Mhz - int totalCpu = hardwareSummary.getCpuMhz() * totalCores; + // Total CPU is based on (# of cores) x Mhz + int totalCpu = hardwareSummary.getCpuMhz() * hardwareSummary.getNumCpuCores(); resourceSummary.setTotalCpu(totalCpu); HostListSummaryQuickStats stats = getHostQuickStats(); From dcbb0ecef53644373ea9345959e754cdeb7526a7 Mon Sep 17 00:00:00 2001 From: Min Chen Date: Wed, 9 Jan 2013 13:46:54 -0800 Subject: [PATCH 08/73] Fix CLOUDSTACK-936: fix nonoss build due to CloudException IdentityProxy removal. --- .../com/cloud/exception/CloudException.java | 8 -- .../CiscoNexusVSMDeviceManagerImpl.java | 94 ++++++++++--------- 2 files changed, 48 insertions(+), 54 deletions(-) diff --git a/api/src/com/cloud/exception/CloudException.java b/api/src/com/cloud/exception/CloudException.java index 2ec61420cee..8f1fa37aac6 100644 --- a/api/src/com/cloud/exception/CloudException.java +++ b/api/src/com/cloud/exception/CloudException.java @@ -56,14 +56,6 @@ public class CloudException extends Exception { return; } - public void addProxyObject(Object voObj, Long id, String idFieldName) { - // Get the VO object's table name. - String tablename = AnnotationHelper.getTableName(voObj); - if (tablename != null) { - addProxyObject(tablename, id, idFieldName); - } - return; - } public ArrayList getIdProxyList() { return idList; diff --git a/plugins/hypervisors/vmware/src/com/cloud/network/CiscoNexusVSMDeviceManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/network/CiscoNexusVSMDeviceManagerImpl.java index d3dcb772af2..528075ea41a 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/network/CiscoNexusVSMDeviceManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/network/CiscoNexusVSMDeviceManagerImpl.java @@ -11,7 +11,7 @@ // 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 +// KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package com.cloud.network; @@ -48,7 +48,7 @@ import com.cloud.utils.cisco.n1kv.vsm.NetconfHelper; public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { @Inject - CiscoNexusVSMDeviceDao _ciscoNexusVSMDeviceDao; + CiscoNexusVSMDeviceDao _ciscoNexusVSMDeviceDao; @Inject ClusterDao _clusterDao; @Inject @@ -65,9 +65,9 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { PortProfileDao _ppDao; @Inject ConfigurationDao _configDao; - + private static final org.apache.log4j.Logger s_logger = Logger.getLogger(ExternalLoadBalancerDeviceManagerImpl.class); - + @DB //public CiscoNexusVSMDeviceVO addCiscoNexusVSM(long clusterId, String ipaddress, String username, String password, ServerResource resource, String vsmName) { public CiscoNexusVSMDeviceVO addCiscoNexusVSM(long clusterId, String ipaddress, String username, String password, String vCenterIpaddr, String vCenterDcName) { @@ -77,7 +77,7 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { // First check if the cluster is of type vmware. If not, // throw an exception. VSMs are tightly integrated with vmware clusters. - + ClusterVO cluster = _clusterDao.findById(clusterId); if (cluster == null) { throw new InvalidParameterValueException("Cluster with specified ID not found!"); @@ -90,21 +90,21 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { // Next, check if the cluster already has a VSM associated with it. // If so, throw an exception disallowing this operation. The user must first // delete the current VSM and then only attempt to add the new one. - + if (_clusterVSMDao.findByClusterId(clusterId) != null) { // We can't have two VSMs for the same cluster. Throw exception. throw new InvalidParameterValueException("Cluster with specified id already has a VSM tied to it. Please remove that first and retry the operation."); } // TODO: Confirm whether we should be checking for VSM reachability here. - + // Next, check if this VSM is reachable. Use the XML-RPC VSM API Java bindings to talk to // the VSM. //NetconfHelper (String ip, String username, String password) NetconfHelper netconfClient; try { - netconfClient = new NetconfHelper(ipaddress, username, password); + netconfClient = new NetconfHelper(ipaddress, username, password); } catch(CloudRuntimeException e) { String msg = "Failed to connect to Nexus VSM " + ipaddress + " with credentials of user " + username; s_logger.error(msg); @@ -118,7 +118,7 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { // First, check if VSM already exists in the table "virtual_supervisor_module". // If it's not there already, create it. // If it's there already, return success. - + // TODO - Right now, we only check if the ipaddress matches for both requests. // We must really check whether every field of the VSM matches. Anyway, the // advantage of our approach for now is that existing infrastructure using @@ -131,7 +131,7 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { throw new CloudRuntimeException(e.getMessage()); } - if (VSMObj == null) { + if (VSMObj == null) { // Create the VSM record. For now, we aren't using the vsmName field. VSMObj = new CiscoNexusVSMDeviceVO(ipaddress, username, password); Transaction txn = Transaction.currentTxn(); @@ -144,7 +144,7 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { throw new CloudRuntimeException(e.getMessage()); } } - + // At this stage, we have a VSM record for sure. Connect the VSM to the cluster Id. long vsmId = _ciscoNexusVSMDeviceDao.getVSMbyIpaddress(ipaddress).getId(); ClusterVSMMapVO connectorObj = new ClusterVSMMapVO(clusterId, vsmId); @@ -157,22 +157,22 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { txn.rollback(); throw new CloudRuntimeException(e.getMessage()); } - + // Now, get a list of all the ESXi servers in this cluster. // This is effectively a select * from host where cluster_id=clusterId; // All ESXi servers are stored in the host table, and their resource // type is vmwareresource. - + //List hosts = _resourceMgr.listAllHostsInCluster(clusterId); - + //TODO: Activate the code below if we make the Nexus VSM a separate resource. // Iterate through each of the hosts in this list. Each host has a host id. // Given this host id, we can reconfigure the in-memory resource representing // the host via the agent manager. Thus we inject VSM related information // into each host's resource. Also, we first configure each resource's // entries in the database to contain this VSM information before the injection. - - //for (HostVO host : hosts) { + + //for (HostVO host : hosts) { // Create a host details VO object and write it out for this hostid. //Long hostid = new Long(vsmId); //DetailVO vsmDetail = new DetailVO(host.getId(), "vsmId", hostid.toString()); @@ -193,28 +193,28 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { //hostDetails.put(ApiConstants.USERNAME, username); //hostDetails.put(ApiConstants.PASSWORD, password); //_agentMrg.send(host.getId(), ) - + return VSMObj; - + } - + @DB - public boolean deleteCiscoNexusVSM(long vsmId) throws ResourceInUseException { + public boolean deleteCiscoNexusVSM(long vsmId) throws ResourceInUseException { CiscoNexusVSMDeviceVO cisconexusvsm = _ciscoNexusVSMDeviceDao.findById(vsmId); if (cisconexusvsm == null) { // This entry is already not present. Return success. return true; } - + // First, check whether this VSM is part of any non-empty cluster. // Search ClusterVSMMap's table for a list of clusters using this vsmId. - + List clusterList = _clusterVSMDao.listByVSMId(vsmId); - - if (clusterList != null) { + + if (clusterList != null) { for (ClusterVSMMapVO record : clusterList) { // If this cluster id has any hosts in it, fail this operation. - Long clusterId = record.getClusterId(); + Long clusterId = record.getClusterId(); List hosts = _resourceMgr.listAllHostsInCluster(clusterId); if (hosts != null && hosts.size() > 0) { for (Host host: hosts) { @@ -222,26 +222,26 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { s_logger.info("Non-empty cluster with id" + clusterId + "still has a host that uses this VSM. Please empty the cluster first"); throw new ResourceInUseException("Non-empty cluster with id" + clusterId + "still has a host that uses this VSM. Please empty the cluster first"); } - } + } } } } - + // Iterate through the cluster list again, this time, delete the VSM. Transaction txn = Transaction.currentTxn(); try { txn.start(); - // Remove the VSM entry in CiscoNexusVSMDeviceVO's table. + // Remove the VSM entry in CiscoNexusVSMDeviceVO's table. _ciscoNexusVSMDeviceDao.remove(vsmId); - // Remove the current record as well from ClusterVSMMapVO's table. + // Remove the current record as well from ClusterVSMMapVO's table. _clusterVSMDao.removeByVsmId(vsmId); // There are no hosts at this stage in the cluster, so we don't need - // to notify any resources or remove host details. - txn.commit(); + // to notify any resources or remove host details. + txn.commit(); } catch (Exception e) { - s_logger.info("Caught exception when trying to delete VSM record.." + e.getMessage()); + s_logger.info("Caught exception when trying to delete VSM record.." + e.getMessage()); throw new CloudRuntimeException("Failed to delete VSM"); - } + } return true; } @@ -249,10 +249,10 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { public CiscoNexusVSMDeviceVO enableCiscoNexusVSM(long vsmId) { CiscoNexusVSMDeviceVO cisconexusvsm = _ciscoNexusVSMDeviceDao.findById(vsmId); if (cisconexusvsm == null) { - throw new InvalidParameterValueException("Invalid vsm Id specified"); + throw new InvalidParameterValueException("Invalid vsm Id specified"); } // Else, check if this db record shows that this VSM is enabled or not. - if (cisconexusvsm.getvsmDeviceState() == CiscoNexusVSMDeviceVO.VSMDeviceState.Disabled) { + if (cisconexusvsm.getvsmDeviceState() == CiscoNexusVSMDeviceVO.VSMDeviceState.Disabled) { // it's currently disabled. So change it to enabled and write it out to the db. cisconexusvsm.setVsmDeviceState(CiscoNexusVSMDeviceVO.VSMDeviceState.Enabled); Transaction txn = Transaction.currentTxn(); @@ -265,18 +265,18 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { throw new CloudRuntimeException(e.getMessage()); } } - + return cisconexusvsm; } - - @DB + + @DB public CiscoNexusVSMDeviceVO disableCiscoNexusVSM(long vsmId) { CiscoNexusVSMDeviceVO cisconexusvsm = _ciscoNexusVSMDeviceDao.findById(vsmId); if (cisconexusvsm == null) { - throw new InvalidParameterValueException("Invalid vsm Id specified"); + throw new InvalidParameterValueException("Invalid vsm Id specified"); } // Else, check if this db record shows that this VSM is enabled or not. - if (cisconexusvsm.getvsmDeviceState() == CiscoNexusVSMDeviceVO.VSMDeviceState.Enabled) { + if (cisconexusvsm.getvsmDeviceState() == CiscoNexusVSMDeviceVO.VSMDeviceState.Enabled) { // it's currently disabled. So change it to enabled and write it out to the db. cisconexusvsm.setVsmDeviceState(CiscoNexusVSMDeviceVO.VSMDeviceState.Disabled); Transaction txn = Transaction.currentTxn(); @@ -289,15 +289,15 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { throw new CloudRuntimeException(e.getMessage()); } } - + return cisconexusvsm; } - + @DB public CiscoNexusVSMDeviceVO getCiscoVSMbyVSMId(long vsmId) { return _ciscoNexusVSMDeviceDao.findById(vsmId); } - + @DB public CiscoNexusVSMDeviceVO getCiscoVSMbyClusId(long clusterId) { ClusterVSMMapVO mapVO = _clusterVSMDao.findByClusterId(clusterId); @@ -309,12 +309,12 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { CiscoNexusVSMDeviceVO result = _ciscoNexusVSMDeviceDao.findById(mapVO.getVsmId()); return result; } - + public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) { // TODO Auto-generated method stub return null; } - + @DB public boolean vliadateVsmCluster(String vsmIp, String vsmUser, String vsmPassword, long clusterId, String clusterName) throws ResourceInUseException { // Check if we're associating a Cisco Nexus VSM with a vmware cluster. @@ -342,7 +342,9 @@ public abstract class CiscoNexusVSMDeviceManagerImpl extends AdapterBase { s_logger.error("Failed to add cluster: specified Nexus VSM is already associated with another cluster"); _clusterDao.remove(clusterId); ResourceInUseException ex = new ResourceInUseException("Failed to add cluster: specified Nexus VSM is already associated with another cluster with specified Id"); - ex.addProxyObject("cluster", clusterList.get(0).getClusterId(), "clusterId"); + // get clusterUuid to report error + ClusterVO cluster = _clusterDao.findById(clusterList.get(0).getClusterId()); + ex.addProxyObject(cluster.getUuid()); throw ex; } } From 72693ea382bd064f4e227faab2f61561b7683149 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 9 Jan 2013 17:10:36 -0800 Subject: [PATCH 09/73] server: Fix ApiServer init method, we won't use cfg files and it's not used there Signed-off-by: Rohit Yadav --- server/src/com/cloud/api/ApiServer.java | 9 ++++----- server/src/com/cloud/servlet/CloudStartupServlet.java | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 56cef123e2c..dfb47faa011 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -166,23 +166,22 @@ public class ApiServer implements HttpRequestHandler { super(); } - public static void initApiServer(String[] apiConfig) { + public static void initApiServer() { if (s_instance == null) { //Injection will create ApiServer and all its fields which have @Inject s_instance = ComponentLocator.inject(ApiServer.class); - s_instance.init(apiConfig); + s_instance.init(); } } public static ApiServer getInstance() { - // Assumption: CloudStartupServlet would initialize ApiServer if (s_instance == null) { - s_logger.fatal("ApiServer instance failed to initialize"); + ApiServer.initApiServer(); } return s_instance; } - public void init(String[] apiConfig) { + public void init() { BaseCmd.setComponents(new ApiResponseHelper()); BaseListCmd.configure(); diff --git a/server/src/com/cloud/servlet/CloudStartupServlet.java b/server/src/com/cloud/servlet/CloudStartupServlet.java index 389bd26bc4b..484c7bf56aa 100755 --- a/server/src/com/cloud/servlet/CloudStartupServlet.java +++ b/server/src/com/cloud/servlet/CloudStartupServlet.java @@ -48,7 +48,7 @@ public class CloudStartupServlet extends HttpServlet implements ServletContextLi s_locator = ComponentLocator.getLocator(ManagementServer.Name); ManagementServer ms = (ManagementServer)ComponentLocator.getComponent(ManagementServer.Name); ms.enableAdminUser("password"); - ApiServer.initApiServer(ms.getPropertiesFiles()); + ApiServer.initApiServer(); } catch (InvalidParameterValueException ipve) { s_logger.error("Exception starting management server ", ipve); throw new ServletException (ipve.getMessage()); From 3dc7626ebc80c5bf87e43fbf341039e027237f27 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 9 Jan 2013 17:18:06 -0800 Subject: [PATCH 10/73] api: Comment out @APICommand annotation for api cmd classes that are unknown Signed-off-by: Rohit Yadav --- api/src/com/cloud/api/commands/CreatePrivateNetworkCmd.java | 2 +- api/src/com/cloud/api/commands/DestroyConsoleProxyCmd.java | 2 +- .../cloud/api/commands/ListRecurringSnapshotScheduleCmd.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/com/cloud/api/commands/CreatePrivateNetworkCmd.java b/api/src/com/cloud/api/commands/CreatePrivateNetworkCmd.java index 92c7ac58be0..b140ac75ec3 100644 --- a/api/src/com/cloud/api/commands/CreatePrivateNetworkCmd.java +++ b/api/src/com/cloud/api/commands/CreatePrivateNetworkCmd.java @@ -31,7 +31,7 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.network.Network; import com.cloud.user.UserContext; -@APICommand(description="Creates a private network", responseObject=NetworkResponse.class) +//@APICommand(description="Creates a private network", responseObject=NetworkResponse.class) public class CreatePrivateNetworkCmd extends BaseAsyncCreateCmd { public static final Logger s_logger = Logger.getLogger(CreatePrivateNetworkCmd.class.getName()); diff --git a/api/src/com/cloud/api/commands/DestroyConsoleProxyCmd.java b/api/src/com/cloud/api/commands/DestroyConsoleProxyCmd.java index 17bafb1918d..80269075744 100644 --- a/api/src/com/cloud/api/commands/DestroyConsoleProxyCmd.java +++ b/api/src/com/cloud/api/commands/DestroyConsoleProxyCmd.java @@ -25,7 +25,7 @@ import com.cloud.event.EventTypes; import com.cloud.user.Account; import com.cloud.user.UserContext; -@APICommand(description="Destroys console proxy", responseObject=SuccessResponse.class) +//@APICommand(description="Destroys console proxy", responseObject=SuccessResponse.class) public class DestroyConsoleProxyCmd extends BaseAsyncCmd { public static final Logger s_logger = Logger.getLogger(DestroyConsoleProxyCmd.class.getName()); diff --git a/api/src/com/cloud/api/commands/ListRecurringSnapshotScheduleCmd.java b/api/src/com/cloud/api/commands/ListRecurringSnapshotScheduleCmd.java index 41f28f93110..709da6af30c 100644 --- a/api/src/com/cloud/api/commands/ListRecurringSnapshotScheduleCmd.java +++ b/api/src/com/cloud/api/commands/ListRecurringSnapshotScheduleCmd.java @@ -27,7 +27,7 @@ import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.SnapshotScheduleResponse; import com.cloud.storage.snapshot.SnapshotSchedule; -@APICommand(description="Lists recurring snapshot schedule", responseObject=SnapshotScheduleResponse.class) +//@APICommand(description="Lists recurring snapshot schedule", responseObject=SnapshotScheduleResponse.class) public class ListRecurringSnapshotScheduleCmd extends BaseListCmd { private static final String s_name = "listrecurringsnapshotscheduleresponse"; From 657fb6ac0becd99ea7a1e99e0423c12e9657d0ae Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 9 Jan 2013 17:19:14 -0800 Subject: [PATCH 11/73] ApiServer: Don't depend on plugin for apiname:cmd class mapping Signed-off-by: Rohit Yadav --- server/src/com/cloud/api/ApiServer.java | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index dfb47faa011..519908daf25 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -60,7 +60,6 @@ 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; @@ -134,8 +133,6 @@ import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CSExceptionErrorCode; import com.cloud.uuididentity.dao.IdentityDao; -import org.reflections.Reflections; - public class ApiServer implements HttpRequestHandler { private static final Logger s_logger = Logger.getLogger(ApiServer.class.getName()); private static final Logger s_accessLogger = Logger.getLogger("apiserver." + ApiServer.class.getName()); @@ -150,15 +147,13 @@ public class ApiServer implements HttpRequestHandler { @Inject(adapter = APIAccessChecker.class) protected Adapters _apiAccessCheckers; - @Inject(adapter = ApiDiscoveryService.class) - protected Adapters _apiDiscoveryServices; private Account _systemAccount = null; private User _systemUser = null; private static int _workerCount = 0; private static ApiServer s_instance = null; private static final DateFormat _dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); - private Map> _apiNameCmdClassMap = new HashMap>(); + private static Map> _apiNameCmdClassMap = new HashMap>(); private static ExecutorService _executor = new ThreadPoolExecutor(10, 150, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(), new NamedThreadFactory("ApiServer")); @@ -202,13 +197,16 @@ public class ApiServer implements HttpRequestHandler { } } - for (ApiDiscoveryService discoveryService: _apiDiscoveryServices) { - _apiNameCmdClassMap.putAll(discoveryService.getApiNameCmdClassMapping()); - } + Set> cmdClasses = ReflectUtil.getClassesWithAnnotation(APICommand.class, + new String[]{"org.apache.cloudstack.api", "com.cloud.api"}); - if (_apiNameCmdClassMap.size() == 0) { - s_logger.fatal("ApiServer failed to generate apiname, cmd class mappings." - + "Please check and enable at least one ApiDiscovery adapter."); + 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); } encodeApiResponse = Boolean.valueOf(configDao.getValue(Config.EncodeApiResponse.key())); From d13cc7e7e49a54e2fdd177c4fe4ce45739b4736d Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 9 Jan 2013 17:19:49 -0800 Subject: [PATCH 12/73] ApiDiscoveryService: Use only as pluggable service Remove usage and impl as adapter. We have duplicate code that generates apiname:cmd class maps which is unavoidable right now as: - Plugin should not depend on ApiServer or any other component - cloud-utils cannot depend on cloud-api for the APICommand annotation - Use java reflect to create a static method in cloud-utils that does the job would be unsafe. Signed-off-by: Rohit Yadav --- .../discovery/ApiDiscoveryService.java | 6 +-- client/tomcatconf/components.xml.in | 3 -- .../discovery/ApiDiscoveryServiceImpl.java | 51 +++++-------------- 3 files changed, 14 insertions(+), 46 deletions(-) diff --git a/api/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java b/api/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java index 12206949db3..96ea3ee4d34 100644 --- a/api/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java +++ b/api/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java @@ -16,14 +16,10 @@ // 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 { +public interface ApiDiscoveryService extends PluggableService { ListResponse listApis(); - Map> getApiNameCmdClassMapping(); } diff --git a/client/tomcatconf/components.xml.in b/client/tomcatconf/components.xml.in index b9feed15a88..b779c860cc2 100755 --- a/client/tomcatconf/components.xml.in +++ b/client/tomcatconf/components.xml.in @@ -56,9 +56,6 @@ under the License. - - - 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 5363e559a5f..ea6b206fa44 100644 --- a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java +++ b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java @@ -17,7 +17,6 @@ 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; @@ -30,7 +29,6 @@ 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; @@ -39,36 +37,28 @@ import java.util.Map; import java.util.Set; @Local(value = ApiDiscoveryService.class) -public class ApiDiscoveryServiceImpl extends AdapterBase implements ApiDiscoveryService { - +public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { private static final Logger s_logger = Logger.getLogger(ApiDiscoveryServiceImpl.class); - private Map> _apiNameCmdClassMap; - private ListResponse _discoveryResponse; + + private ListResponse _discoveryResponse = new ListResponse(); + + private Map> _apiNameCmdClassMap = new HashMap>(); protected ApiDiscoveryServiceImpl() { super(); + generateApiNameCmdClassMap(); + cacheListApiResponse(); } - private void generateApiNameCmdClassMapping() { - _apiNameCmdClassMap = new HashMap>(); - Set> cmdClasses = ReflectUtil.getClassesWithAnnotation(APICommand.class, new String[]{"org.apache.cloudstack.api", "com.cloud.api"}); + private void generateApiNameCmdClassMap() { + Set> 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); - } + for(Class cmdClass: cmdClasses) + _apiNameCmdClassMap.put(cmdClass.getAnnotation(APICommand.class).name(), cmdClass); } - private void precacheListApiResponse() { - - if(_apiNameCmdClassMap == null) - return; - - _discoveryResponse = new ListResponse(); + private void cacheListApiResponse() { List apiDiscoveryResponses = new ArrayList(); @@ -117,21 +107,6 @@ public class ApiDiscoveryServiceImpl extends AdapterBase implements ApiDiscovery _discoveryResponse.setResponses(apiDiscoveryResponses); } - @Override - public boolean configure(String name, Map params) - throws ConfigurationException { - super.configure(name, params); - - generateApiNameCmdClassMapping(); - precacheListApiResponse(); - - return true; - } - - public Map> getApiNameCmdClassMapping() { - return _apiNameCmdClassMap; - } - @Override public ListResponse listApis() { return _discoveryResponse; From 7f8262d45e9f2cdaa5d8f1aee0df61dab9573dd7 Mon Sep 17 00:00:00 2001 From: Min Chen Date: Wed, 9 Jan 2013 17:40:58 -0800 Subject: [PATCH 13/73] Remove IdentityProxy and IdentityTypeAdapter class. Signed-off-by: Min Chen --- .../api/commands/CreatePrivateNetworkCmd.java | 5 +- .../com/cloud/exception/CloudException.java | 2 - .../cloudstack/api/BaseAsyncCreateCmd.java | 15 +++- .../org/apache/cloudstack/api/BaseCmd.java | 2 - .../apache/cloudstack/api/BaseResponse.java | 6 +- .../admin/autoscale/CreateCounterCmd.java | 6 +- .../network/AddNetworkServiceProviderCmd.java | 5 +- .../network/CreatePhysicalNetworkCmd.java | 5 +- .../router/CreateVirtualRouterElementCmd.java | 6 +- .../admin/usage/AddTrafficTypeCmd.java | 5 +- .../admin/vpc/CreatePrivateGatewayCmd.java | 5 +- .../admin/vpc/CreateVPCOfferingCmd.java | 5 +- .../user/address/AssociateIPAddrCmd.java | 4 +- .../autoscale/CreateAutoScalePolicyCmd.java | 5 +- .../autoscale/CreateAutoScaleVmGroupCmd.java | 5 +- .../CreateAutoScaleVmProfileCmd.java | 6 +- .../user/autoscale/CreateConditionCmd.java | 6 +- .../user/firewall/CreateFirewallRuleCmd.java | 4 +- .../firewall/CreatePortForwardingRuleCmd.java | 4 +- .../CreateLBStickinessPolicyCmd.java | 5 +- .../CreateLoadBalancerRuleCmd.java | 4 +- .../user/nat/CreateIpForwardingRuleCmd.java | 4 +- .../user/network/CreateNetworkACLCmd.java | 5 +- .../user/project/CreateProjectCmd.java | 4 +- .../user/snapshot/CreateSnapshotCmd.java | 4 +- .../user/template/CreateTemplateCmd.java | 7 +- .../api/command/user/vm/DeployVMCmd.java | 4 +- .../command/user/volume/CreateVolumeCmd.java | 4 +- .../user/vpc/CreateStaticRouteCmd.java | 5 +- .../api/command/user/vpc/CreateVPCCmd.java | 6 +- .../api/command/user/vpn/AddVpnUserCmd.java | 4 +- .../user/vpn/CreateRemoteAccessVpnCmd.java | 9 ++- .../user/vpn/CreateVpnConnectionCmd.java | 4 +- .../user/vpn/CreateVpnCustomerGatewayCmd.java | 3 - .../command/user/vpn/CreateVpnGatewayCmd.java | 4 - .../user/vpn/DeleteVpnConnectionCmd.java | 3 - .../user/vpn/DeleteVpnCustomerGatewayCmd.java | 3 - .../command/user/vpn/DeleteVpnGatewayCmd.java | 3 - .../user/vpn/ResetVpnConnectionCmd.java | 3 - .../user/vpn/UpdateVpnCustomerGatewayCmd.java | 6 +- .../api/response/CapacityResponse.java | 1 - .../api/response/CreateCmdResponse.java | 18 ++--- .../api/response/ResourceCountResponse.java | 1 - .../cloudstack/api/response/S3Response.java | 9 +-- server/src/com/cloud/api/ApiGsonHelper.java | 2 - .../com/cloud/api/ApiResponseGsonHelper.java | 4 +- .../src/com/cloud/api/ApiResponseHelper.java | 2 +- server/src/com/cloud/api/ApiServer.java | 9 ++- .../com/cloud/api/IdentityTypeAdapter.java | 80 ------------------- .../api/response/ApiResponseSerializer.java | 46 +++-------- .../cloud/server/ConfigurationServerImpl.java | 2 - utils/src/com/cloud/utils/IdentityProxy.java | 60 -------------- .../exception/RuntimeCloudException.java | 1 - 53 files changed, 84 insertions(+), 346 deletions(-) delete mode 100644 server/src/com/cloud/api/IdentityTypeAdapter.java delete mode 100644 utils/src/com/cloud/utils/IdentityProxy.java diff --git a/api/src/com/cloud/api/commands/CreatePrivateNetworkCmd.java b/api/src/com/cloud/api/commands/CreatePrivateNetworkCmd.java index b140ac75ec3..263f023b3e5 100644 --- a/api/src/com/cloud/api/commands/CreatePrivateNetworkCmd.java +++ b/api/src/com/cloud/api/commands/CreatePrivateNetworkCmd.java @@ -153,6 +153,7 @@ public class CreatePrivateNetworkCmd extends BaseAsyncCreateCmd { if (result != null) { this.setEntityId(result.getId()); + this.setEntityUuid(result.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create a Private network"); } @@ -190,8 +191,4 @@ public class CreatePrivateNetworkCmd extends BaseAsyncCreateCmd { } - @Override - public String getEntityTable() { - return "networks"; - } } diff --git a/api/src/com/cloud/exception/CloudException.java b/api/src/com/cloud/exception/CloudException.java index 8f1fa37aac6..036cb1b8adc 100644 --- a/api/src/com/cloud/exception/CloudException.java +++ b/api/src/com/cloud/exception/CloudException.java @@ -16,10 +16,8 @@ // under the License. package com.cloud.exception; -import com.cloud.utils.IdentityProxy; import java.util.ArrayList; import com.cloud.utils.exception.CSExceptionErrorCode; -import com.cloud.utils.AnnotationHelper; /** * by the API response serializer. Any exceptions that are thrown by diff --git a/api/src/org/apache/cloudstack/api/BaseAsyncCreateCmd.java b/api/src/org/apache/cloudstack/api/BaseAsyncCreateCmd.java index ad9f4c6b31f..1f2d3f17beb 100644 --- a/api/src/org/apache/cloudstack/api/BaseAsyncCreateCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseAsyncCreateCmd.java @@ -25,6 +25,8 @@ public abstract class BaseAsyncCreateCmd extends BaseAsyncCmd { @Parameter(name = "id", type = CommandType.LONG) private Long id; + private String uuid; + public abstract void create() throws ResourceAllocationException; public Long getEntityId() { @@ -35,14 +37,19 @@ public abstract class BaseAsyncCreateCmd extends BaseAsyncCmd { this.id = id; } - public abstract String getEntityTable(); + public String getEntityUuid() { + return uuid; + } - public String getResponse(long jobId, long objectId, String objectEntityTable) { + public void setEntityUuid(String uuid) { + this.uuid = uuid; + } + + public String getResponse(long jobId, String objectUuid) { CreateCmdResponse response = new CreateCmdResponse(); AsyncJob job = _entityMgr.findById(AsyncJob.class, jobId); response.setJobId(job.getUuid()); - response.setId(objectId); - response.setIdEntityTable(objectEntityTable); + response.setId(objectUuid); response.setResponseName(getCommandName()); return _responseGenerator.toSerializedString(response, getResponseType()); } diff --git a/api/src/org/apache/cloudstack/api/BaseCmd.java b/api/src/org/apache/cloudstack/api/BaseCmd.java index d964e70b84f..3399784d2a2 100644 --- a/api/src/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseCmd.java @@ -19,7 +19,6 @@ package org.apache.cloudstack.api; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -64,7 +63,6 @@ import com.cloud.user.Account; import com.cloud.user.AccountService; import com.cloud.user.DomainService; import com.cloud.user.ResourceLimitService; -import com.cloud.utils.IdentityProxy; import com.cloud.utils.Pair; import com.cloud.utils.component.ComponentLocator; import com.cloud.vm.BareMetalVmService; diff --git a/api/src/org/apache/cloudstack/api/BaseResponse.java b/api/src/org/apache/cloudstack/api/BaseResponse.java index 28ca6b8c2de..01f1be3253b 100644 --- a/api/src/org/apache/cloudstack/api/BaseResponse.java +++ b/api/src/org/apache/cloudstack/api/BaseResponse.java @@ -17,7 +17,6 @@ package org.apache.cloudstack.api; import org.apache.cloudstack.api.ApiConstants; -import com.cloud.utils.IdentityProxy; import org.apache.cloudstack.api.ResponseObject; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; @@ -46,6 +45,7 @@ public abstract class BaseResponse implements ResponseObject { this.objectName = objectName; } + @Override public String getObjectId() { return null; } @@ -56,18 +56,22 @@ public abstract class BaseResponse implements ResponseObject { @SerializedName(ApiConstants.JOB_STATUS) @Param(description="the current status of the latest async job acting on this object") private Integer jobStatus; + @Override public String getJobId() { return jobId; } + @Override public void setJobId(String jobId) { this.jobId = jobId; } + @Override public Integer getJobStatus() { return jobStatus; } + @Override public void setJobStatus(Integer jobStatus) { this.jobStatus = jobStatus; } diff --git a/api/src/org/apache/cloudstack/api/command/admin/autoscale/CreateCounterCmd.java b/api/src/org/apache/cloudstack/api/command/admin/autoscale/CreateCounterCmd.java index 7369a6f6d08..a119d0f44bf 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/autoscale/CreateCounterCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/autoscale/CreateCounterCmd.java @@ -81,6 +81,7 @@ public class CreateCounterCmd extends BaseAsyncCreateCmd { if (ctr != null) { this.setEntityId(ctr.getId()); + this.setEntityUuid(ctr.getUuid()); CounterResponse response = _responseGenerator.createCounterResponse(ctr); response.setResponseName(getCommandName()); this.setResponseObject(response); @@ -113,8 +114,5 @@ public class CreateCounterCmd extends BaseAsyncCreateCmd { return Account.ACCOUNT_ID_SYSTEM; } - @Override - public String getEntityTable() { - return "counter"; - } + } diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkServiceProviderCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkServiceProviderCmd.java index b6518d8eb59..6d4b962d4a1 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkServiceProviderCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkServiceProviderCmd.java @@ -59,10 +59,6 @@ public class AddNetworkServiceProviderCmd extends BaseAsyncCreateCmd { @Parameter(name=ApiConstants.SERVICE_LIST, type=CommandType.LIST, collectionType = CommandType.STRING, description="the list of services to be enabled for this physical network service provider") private List enabledServices; - @Override - public String getEntityTable() { - return "physical_network_service_providers"; - } ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -116,6 +112,7 @@ public class AddNetworkServiceProviderCmd extends BaseAsyncCreateCmd { PhysicalNetworkServiceProvider result = _networkService.addProviderToPhysicalNetwork(getPhysicalNetworkId(), getProviderName(), getDestinationPhysicalNetworkId(), getEnabledServices()); if (result != null) { setEntityId(result.getId()); + setEntityUuid(result.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to add service provider entity to physical network"); } diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/CreatePhysicalNetworkCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/CreatePhysicalNetworkCmd.java index dd3f3231351..f56ae7dbf50 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/network/CreatePhysicalNetworkCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/network/CreatePhysicalNetworkCmd.java @@ -79,10 +79,6 @@ public class CreatePhysicalNetworkCmd extends BaseAsyncCreateCmd { return tags; } - @Override - public String getEntityTable() { - return "physical_network"; - } public Long getZoneId() { return zoneId; @@ -164,6 +160,7 @@ public class CreatePhysicalNetworkCmd extends BaseAsyncCreateCmd { PhysicalNetwork result = _networkService.createPhysicalNetwork(getZoneId(),getVlan(),getNetworkSpeed(), getIsolationMethods(),getBroadcastDomainRange(),getDomainId(), getTags(), getNetworkName()); if (result != null) { setEntityId(result.getId()); + setEntityUuid(result.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create physical network entity"); } diff --git a/api/src/org/apache/cloudstack/api/command/admin/router/CreateVirtualRouterElementCmd.java b/api/src/org/apache/cloudstack/api/command/admin/router/CreateVirtualRouterElementCmd.java index 545218f0364..f6a7b744ca3 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/router/CreateVirtualRouterElementCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/router/CreateVirtualRouterElementCmd.java @@ -53,10 +53,7 @@ public class CreateVirtualRouterElementCmd extends BaseAsyncCreateCmd { this.nspId = nspId; } - @Override - public String getEntityTable() { - return "virtual_router_providers"; - } + public Long getNspId() { return nspId; @@ -94,6 +91,7 @@ public class CreateVirtualRouterElementCmd extends BaseAsyncCreateCmd { VirtualRouterProvider result = _service.addElement(getNspId(), VirtualRouterProviderType.VirtualRouter); if (result != null) { setEntityId(result.getId()); + setEntityUuid(result.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to add Virtual Router entity to physical network"); } diff --git a/api/src/org/apache/cloudstack/api/command/admin/usage/AddTrafficTypeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/usage/AddTrafficTypeCmd.java index 1759ff7e8e6..5dca9d2d4c1 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/usage/AddTrafficTypeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/usage/AddTrafficTypeCmd.java @@ -66,10 +66,6 @@ public class AddTrafficTypeCmd extends BaseAsyncCreateCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - @Override - public String getEntityTable() { - return "physical_network_traffic_types"; - } public Long getPhysicalNetworkId() { return physicalNetworkId; @@ -136,6 +132,7 @@ public class AddTrafficTypeCmd extends BaseAsyncCreateCmd { PhysicalNetworkTrafficType result = _networkService.addTrafficTypeToPhysicalNetwork(getPhysicalNetworkId(), getTrafficType(), getXenLabel(), getKvmLabel(), getVmwareLabel(), getSimulatorLabel(), getVlan()); if (result != null) { setEntityId(result.getId()); + setEntityUuid(result.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to add traffic type to physical network"); } diff --git a/api/src/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayCmd.java index 7950b877cec..5bb76ab034b 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayCmd.java @@ -123,6 +123,7 @@ public class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd { if (result != null) { this.setEntityId(result.getId()); + this.setEntityUuid(result.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create private gateway"); } @@ -156,10 +157,6 @@ public class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd { return "creating private gateway"; } - @Override - public String getEntityTable() { - return "vpc_gateways"; - } @Override diff --git a/api/src/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java index a0abe99f826..273f7c05233 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java @@ -70,6 +70,7 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd{ VpcOffering vpcOff = _vpcService.createVpcOffering(getVpcOfferingName(), getDisplayText(), getSupportedServices()); if (vpcOff != null) { this.setEntityId(vpcOff.getId()); + this.setEntityUuid(vpcOff.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create a VPC offering"); } @@ -87,10 +88,6 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd{ } } - @Override - public String getEntityTable() { - return "vpc_offerings"; - } @Override public String getEventType() { diff --git a/api/src/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java b/api/src/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java index 7d4e44bb507..024ba74e8b4 100644 --- a/api/src/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/address/AssociateIPAddrCmd.java @@ -87,9 +87,6 @@ public class AssociateIPAddrCmd extends BaseAsyncCreateCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getEntityTable() { - return "user_ip_address"; - } public String getAccountName() { if (accountName != null) { @@ -220,6 +217,7 @@ public class AssociateIPAddrCmd extends BaseAsyncCreateCmd { if (ip != null) { this.setEntityId(ip.getId()); + this.setEntityUuid(ip.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to allocate ip address"); } diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScalePolicyCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScalePolicyCmd.java index db3aaa6dc5d..e92721d77bf 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScalePolicyCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScalePolicyCmd.java @@ -62,10 +62,6 @@ public class CreateAutoScalePolicyCmd extends BaseAsyncCreateCmd { private Long conditionDomainId; private Long conditionAccountId; - @Override - public String getEntityTable() { - return "autoscale_policies"; - } public int getDuration() { return duration; @@ -159,6 +155,7 @@ public class CreateAutoScalePolicyCmd extends BaseAsyncCreateCmd { AutoScalePolicy result = _autoScaleService.createAutoScalePolicy(this); if (result != null) { this.setEntityId(result.getId()); + this.setEntityUuid(result.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create AutoScale Policy"); } diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java index 6297888f5d3..e3d47a09c7d 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmGroupCmd.java @@ -72,10 +72,6 @@ public class CreateAutoScaleVmGroupCmd extends BaseAsyncCreateCmd { // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// - @Override - public String getEntityTable() { - return "autoscale_vmgroups"; - } public int getMinMembers() { return minMembers; @@ -161,6 +157,7 @@ public class CreateAutoScaleVmGroupCmd extends BaseAsyncCreateCmd { AutoScaleVmGroup result = _autoScaleService.createAutoScaleVmGroup(this); if (result != null) { this.setEntityId(result.getId()); + this.setEntityUuid(result.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create Autoscale Vm Group"); } diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java index daa48501c53..25bb03b778f 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java @@ -86,10 +86,7 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd { private Long domainId; private Long accountId; - @Override - public String getEntityTable() { - return "autoscale_vmprofiles"; - } + public Long getDomainId() { if (domainId == null) { @@ -232,6 +229,7 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd { AutoScaleVmProfile result = _autoScaleService.createAutoScaleVmProfile(this); if (result != null) { this.setEntityId(result.getId()); + this.setEntityUuid(result.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create Autoscale Vm Profile"); } diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java index a9524714ffa..58926f2a4ff 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java @@ -72,6 +72,7 @@ public class CreateConditionCmd extends BaseAsyncCreateCmd { if (condition != null) { this.setEntityId(condition.getId()); + this.setEntityUuid(condition.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create condition."); } @@ -146,8 +147,5 @@ public class CreateConditionCmd extends BaseAsyncCreateCmd { return accountId; } - @Override - public String getEntityTable() { - return "conditions"; - } + } diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java index 803301febe9..7039b417ced 100644 --- a/api/src/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java @@ -80,9 +80,6 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// - public String getEntityTable() { - return "firewall_rules"; - } public Long getIpAddressId() { return ipAddressId; @@ -242,6 +239,7 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal try { FirewallRule result = _firewallService.createFirewallRule(this); setEntityId(result.getId()); + setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException ex) { s_logger.info("Network rule conflict: " + ex.getMessage()); s_logger.trace("Network Rule Conflict: ", ex); diff --git a/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java index ecccf032ace..1feefde9a1a 100644 --- a/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/firewall/CreatePortForwardingRuleCmd.java @@ -94,9 +94,6 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// - public String getEntityTable() { - return "firewall_rules"; - } public Long getIpAddressId() { return ipAddressId; @@ -301,6 +298,7 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P try { PortForwardingRule result = _rulesService.createPortForwardingRule(this, virtualMachineId, getOpenFirewall()); setEntityId(result.getId()); + setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException ex) { s_logger.info("Network rule conflict: " , ex); s_logger.trace("Network Rule Conflict: ", ex); diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java index dc80d312769..c01e138c1d1 100644 --- a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLBStickinessPolicyCmd.java @@ -91,9 +91,7 @@ public class CreateLBStickinessPolicyCmd extends BaseAsyncCreateCmd { return paramList; } - public String getEntityTable() { - return "firewall_rules"; - } + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// @@ -141,6 +139,7 @@ public class CreateLBStickinessPolicyCmd extends BaseAsyncCreateCmd { try { StickinessPolicy result = _lbService.createLBStickinessPolicy(this); this.setEntityId(result.getId()); + this.setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException e) { s_logger.warn("Exception: ", e); throw new ServerApiException(BaseCmd.NETWORK_RULE_CONFLICT_ERROR, e.getMessage()); diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java index 4aacc8e19b2..4e76a6b676f 100644 --- a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java @@ -120,9 +120,6 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements return privatePort; } - public String getEntityTable() { - return "firewall_rules"; - } public Long getSourceIpAddressId() { if (publicIpId != null) { @@ -283,6 +280,7 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements try { LoadBalancer result = _lbService.createLoadBalancerRule(this, getOpenFirewall()); this.setEntityId(result.getId()); + this.setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException e) { s_logger.warn("Exception: ", e); throw new ServerApiException(BaseCmd.NETWORK_RULE_CONFLICT_ERROR, e.getMessage()); diff --git a/api/src/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java index e612b84c835..1ce3458dde3 100644 --- a/api/src/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java @@ -75,9 +75,6 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Sta /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getEntityTable() { - return "firewall_rules"; - } public Long getIpAddressId() { return ipAddressId; @@ -151,6 +148,7 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Sta try { StaticNatRule rule = _rulesService.createStaticNatRule(this, getOpenFirewall()); this.setEntityId(rule.getId()); + this.setEntityUuid(rule.getUuid()); } catch (NetworkRuleConflictException e) { s_logger.info("Unable to create Static Nat Rule due to ", e); throw new ServerApiException(BaseCmd.NETWORK_RULE_CONFLICT_ERROR, e.getMessage()); diff --git a/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkACLCmd.java b/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkACLCmd.java index e2aba5b321e..16843b56d67 100644 --- a/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkACLCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkACLCmd.java @@ -86,10 +86,6 @@ public class CreateNetworkACLCmd extends BaseAsyncCreateCmd implements FirewallR // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// - public String getEntityTable() { - return "firewall_rules"; - } - public Long getIpAddressId() { return null; } @@ -262,6 +258,7 @@ public class CreateNetworkACLCmd extends BaseAsyncCreateCmd implements FirewallR try { FirewallRule result = _networkACLService.createNetworkACL(this); setEntityId(result.getId()); + setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException ex) { s_logger.info("Network rule conflict: " + ex.getMessage()); s_logger.trace("Network Rule Conflict: ", ex); diff --git a/api/src/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java b/api/src/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java index 9500a972b36..865f7a0aa99 100644 --- a/api/src/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/project/CreateProjectCmd.java @@ -56,9 +56,6 @@ public class CreateProjectCmd extends BaseAsyncCreateCmd { // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// - public String getEntityTable() { - return "projects"; - } public String getAccountName() { if (accountName != null) { @@ -127,6 +124,7 @@ public class CreateProjectCmd extends BaseAsyncCreateCmd { Project project = _projectService.createProject(getName(), getDisplayText(), getAccountName(), getDomainId()); if (project != null) { this.setEntityId(project.getId()); + this.setEntityUuid(project.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create a project"); } diff --git a/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java index 33469ac4882..14f46540cc3 100644 --- a/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java @@ -65,9 +65,6 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// - public String getEntityTable() { - return "snapshots"; - } public String getAccountName() { return accountName; @@ -153,6 +150,7 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { Snapshot snapshot = _snapshotService.allocSnapshot(getVolumeId(), getPolicyId()); if (snapshot != null) { this.setEntityId(snapshot.getId()); + this.setEntityUuid(snapshot.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create snapshot"); } diff --git a/api/src/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java b/api/src/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java index 65cc8b9e9a4..e72b49b4e4d 100644 --- a/api/src/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java @@ -102,9 +102,6 @@ import com.cloud.user.UserContext; // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// - public String getEntityTable() { - return "vm_template"; - } public Integer getBits() { return bits; @@ -240,13 +237,15 @@ import com.cloud.user.UserContext; public void create() throws ResourceAllocationException { if (isBareMetal()) { _bareMetalVmService.createPrivateTemplateRecord(this, _accountService.getAccount(getEntityOwnerId())); - /*Baremetal creates template record after taking image proceeded, use vmId as entity id here*/ + /*Baremetal creates template record after taking image proceeded, use vmId as entity id and uuid here*/ this.setEntityId(vmId); + this.setEntityUuid(vmId.toString()); } else { VirtualMachineTemplate template = null; template = _userVmService.createPrivateTemplateRecord(this, _accountService.getAccount(getEntityOwnerId())); if (template != null) { this.setEntityId(template.getId()); + this.setEntityUuid(template.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create a template"); diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index 349f4a12d16..28bb80f72d3 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -171,9 +171,6 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getEntityTable() { - return "vm_instance"; - } public String getAccountName() { if (accountName == null) { @@ -446,6 +443,7 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { if (vm != null) { setEntityId(vm.getId()); + setEntityUuid(vm.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to deploy vm"); } diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java index 512685f77f6..04541b9fda7 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java @@ -76,9 +76,6 @@ public class CreateVolumeCmd extends BaseAsyncCreateCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getEntityTable() { - return "volumes"; - } public String getAccountName() { return accountName; @@ -154,6 +151,7 @@ public class CreateVolumeCmd extends BaseAsyncCreateCmd { Volume volume = _storageService.allocVolume(this); if (volume != null) { this.setEntityId(volume.getId()); + this.setEntityUuid(volume.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create volume"); } diff --git a/api/src/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java index 85a0ae45ae8..96de56a5be5 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java @@ -67,6 +67,7 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd{ try { StaticRoute result = _vpcService.createStaticRoute(getGatewayId(), getCidr()); setEntityId(result.getId()); + setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException ex) { s_logger.info("Network rule conflict: " + ex.getMessage()); s_logger.trace("Network rule conflict: ", ex); @@ -74,10 +75,6 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd{ } } - @Override - public String getEntityTable() { - return "static_routes"; - } @Override public String getEventType() { diff --git a/api/src/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java index df16c8edc88..8a2e1f641fb 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java @@ -124,6 +124,7 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd{ getCidr(), getNetworkDomain()); if (vpc != null) { this.setEntityId(vpc.getId()); + this.setEntityUuid(vpc.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create a VPC"); } @@ -157,11 +158,6 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd{ } } - @Override - public String getEntityTable() { - return "vpc"; - } - @Override public String getEventType() { diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java index 674dc6a5809..f2d19a7cce6 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java @@ -103,9 +103,6 @@ public class AddVpnUserCmd extends BaseAsyncCreateCmd { return accountId; } - public String getEntityTable() { - return "vpn_users"; - } @Override public String getEventDescription() { @@ -150,5 +147,6 @@ public class AddVpnUserCmd extends BaseAsyncCreateCmd { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to add vpn user"); } setEntityId(vpnUser.getId()); + setEntityUuid(vpnUser.getUuid()); } } diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/CreateRemoteAccessVpnCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/CreateRemoteAccessVpnCmd.java index 37952f8777a..b517af883c3 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/CreateRemoteAccessVpnCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/CreateRemoteAccessVpnCmd.java @@ -62,10 +62,6 @@ public class CreateRemoteAccessVpnCmd extends BaseAsyncCreateCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getEntityTable() { - return "user_ip_address"; - } - public Long getPublicIpId() { return publicIpId; } @@ -146,6 +142,11 @@ public class CreateRemoteAccessVpnCmd extends BaseAsyncCreateCmd { RemoteAccessVpn vpn = _ravService.createRemoteAccessVpn(publicIpId, ipRange, getOpenFirewall(), getNetworkId()); if (vpn != null) { this.setEntityId(vpn.getServerAddressId()); + // find uuid for server ip address + IpAddress ipAddr = _entityMgr.findById(IpAddress.class, vpn.getServerAddressId()); + if (ipAddr != null) { + this.setEntityUuid(ipAddr.getUuid()); + } } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create remote access vpn"); } diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnConnectionCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnConnectionCmd.java index 7f85fb4ebf6..3dc334d0e2a 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnConnectionCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnConnectionCmd.java @@ -51,9 +51,6 @@ public class CreateVpnConnectionCmd extends BaseAsyncCreateCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getEntityTable() { - return "s2s_vpn_connection"; - } public Long getVpnGatewayId() { return vpnGatewayId; @@ -95,6 +92,7 @@ public class CreateVpnConnectionCmd extends BaseAsyncCreateCmd { Site2SiteVpnConnection conn = _s2sVpnService.createVpnConnection(this); if (conn != null) { this.setEntityId(conn.getId()); + this.setEntityUuid(conn.getUuid()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create site to site vpn connection"); } diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java index 65085182e0c..bde98b0b44b 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java @@ -78,9 +78,6 @@ public class CreateVpnCustomerGatewayCmd extends BaseAsyncCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getEntityTable() { - return "s2s_customer_gateway"; - } public String getName() { return name; diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnGatewayCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnGatewayCmd.java index 89965bd842c..4b405541a90 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnGatewayCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnGatewayCmd.java @@ -47,10 +47,6 @@ public class CreateVpnGatewayCmd extends BaseAsyncCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getEntityTable() { - return "s2s_vpn_gateway"; - } - public Long getVpcId() { return vpcId; } diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteVpnConnectionCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteVpnConnectionCmd.java index a079e8bcc30..23a7793ef88 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteVpnConnectionCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteVpnConnectionCmd.java @@ -44,9 +44,6 @@ public class DeleteVpnConnectionCmd extends BaseAsyncCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getEntityTable() { - return "s2s_vpn_connection"; - } public Long getId() { return id; diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteVpnCustomerGatewayCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteVpnCustomerGatewayCmd.java index ef5ff3db438..181ee3bbc68 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteVpnCustomerGatewayCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteVpnCustomerGatewayCmd.java @@ -43,9 +43,6 @@ public class DeleteVpnCustomerGatewayCmd extends BaseAsyncCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getEntityTable() { - return "s2s_customer_gateway"; - } public Long getId() { return id; diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteVpnGatewayCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteVpnGatewayCmd.java index f9b9e35a420..9ac27d07664 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteVpnGatewayCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/DeleteVpnGatewayCmd.java @@ -43,9 +43,6 @@ public class DeleteVpnGatewayCmd extends BaseAsyncCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getEntityTable() { - return "s2s_vpn_gateway"; - } public Long getId() { return id; diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/ResetVpnConnectionCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/ResetVpnConnectionCmd.java index 0d7632ac1aa..ed28ea5610f 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/ResetVpnConnectionCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/ResetVpnConnectionCmd.java @@ -53,9 +53,6 @@ public class ResetVpnConnectionCmd extends BaseAsyncCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getEntityTable() { - return "s2s_vpn_connection"; - } public Long getDomainId() { return domainId; diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java index f2778e06103..7564129c38f 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java @@ -78,11 +78,7 @@ public class UpdateVpnCustomerGatewayCmd extends BaseAsyncCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getEntityTable() { - return "s2s_customer_gateway"; - } - - public Long getId() { + public Long getId() { return id; } diff --git a/api/src/org/apache/cloudstack/api/response/CapacityResponse.java b/api/src/org/apache/cloudstack/api/response/CapacityResponse.java index 000705813fb..2c98dc9d6ca 100644 --- a/api/src/org/apache/cloudstack/api/response/CapacityResponse.java +++ b/api/src/org/apache/cloudstack/api/response/CapacityResponse.java @@ -17,7 +17,6 @@ package org.apache.cloudstack.api.response; import org.apache.cloudstack.api.ApiConstants; -import com.cloud.utils.IdentityProxy; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.BaseResponse; diff --git a/api/src/org/apache/cloudstack/api/response/CreateCmdResponse.java b/api/src/org/apache/cloudstack/api/response/CreateCmdResponse.java index 3c26324e10b..e4c6c60c5ba 100644 --- a/api/src/org/apache/cloudstack/api/response/CreateCmdResponse.java +++ b/api/src/org/apache/cloudstack/api/response/CreateCmdResponse.java @@ -16,24 +16,16 @@ // under the License. package org.apache.cloudstack.api.response; -import org.apache.cloudstack.api.ApiConstants; -import com.cloud.utils.IdentityProxy; -import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.BaseResponse; public class CreateCmdResponse extends BaseResponse { - @SerializedName(ApiConstants.ID) - private IdentityProxy id = new IdentityProxy(); + private String id; - public Long getId() { - return id.getValue(); + public String getId() { + return id; } - public void setId(Long id) { - this.id.setValue(id); - } - - public void setIdEntityTable(String entityTable) { - this.id.setTableName(entityTable); + public void setId(String id) { + this.id = id; } } diff --git a/api/src/org/apache/cloudstack/api/response/ResourceCountResponse.java b/api/src/org/apache/cloudstack/api/response/ResourceCountResponse.java index 9e62f4ff7f5..7a291945f76 100644 --- a/api/src/org/apache/cloudstack/api/response/ResourceCountResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ResourceCountResponse.java @@ -17,7 +17,6 @@ package org.apache.cloudstack.api.response; import org.apache.cloudstack.api.ApiConstants; -import com.cloud.utils.IdentityProxy; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.BaseResponse; diff --git a/api/src/org/apache/cloudstack/api/response/S3Response.java b/api/src/org/apache/cloudstack/api/response/S3Response.java index 5dd0ef0e041..4dab2175a3a 100644 --- a/api/src/org/apache/cloudstack/api/response/S3Response.java +++ b/api/src/org/apache/cloudstack/api/response/S3Response.java @@ -19,7 +19,6 @@ package org.apache.cloudstack.api.response; import com.cloud.serializer.Param; -import com.cloud.utils.IdentityProxy; import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.BaseResponse; @@ -29,7 +28,7 @@ public class S3Response extends BaseResponse { @SerializedName(ID) @Param(description = "The ID of the S3 configuration") - private IdentityProxy id = new IdentityProxy("s3"); + private String id; @SerializedName(S3_ACCESS_KEY) @Param(description = "The S3 access key") @@ -135,11 +134,11 @@ public class S3Response extends BaseResponse { @Override public String getObjectId() { - return this.id.getValue().toString(); + return this.id; } - public void setObjectId(Long id) { - this.id.setValue(id); + public void setObjectId(String id) { + this.id = id; } public String getAccessKey() { diff --git a/server/src/com/cloud/api/ApiGsonHelper.java b/server/src/com/cloud/api/ApiGsonHelper.java index 6e64f7124e0..6163860f0c8 100644 --- a/server/src/com/cloud/api/ApiGsonHelper.java +++ b/server/src/com/cloud/api/ApiGsonHelper.java @@ -17,7 +17,6 @@ package com.cloud.api; import com.google.gson.GsonBuilder; -import com.cloud.utils.IdentityProxy; import org.apache.cloudstack.api.ResponseObject; import java.util.Map; @@ -28,7 +27,6 @@ public class ApiGsonHelper { s_gBuilder = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); s_gBuilder.setVersion(1.3); s_gBuilder.registerTypeAdapter(ResponseObject.class, new ResponseObjectTypeAdapter()); - s_gBuilder.registerTypeAdapter(IdentityProxy.class, new IdentityTypeAdapter()); s_gBuilder.registerTypeAdapter(Map.class, new StringMapTypeAdapter()); } diff --git a/server/src/com/cloud/api/ApiResponseGsonHelper.java b/server/src/com/cloud/api/ApiResponseGsonHelper.java index c71193e8908..6bccf9a12af 100644 --- a/server/src/com/cloud/api/ApiResponseGsonHelper.java +++ b/server/src/com/cloud/api/ApiResponseGsonHelper.java @@ -17,7 +17,6 @@ package com.cloud.api; import com.google.gson.GsonBuilder; -import com.cloud.utils.IdentityProxy; import org.apache.cloudstack.api.ResponseObject; /** @@ -25,13 +24,12 @@ import org.apache.cloudstack.api.ResponseObject; */ public class ApiResponseGsonHelper { private static final GsonBuilder s_gBuilder; - + static { s_gBuilder = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); s_gBuilder.setVersion(1.3); s_gBuilder.registerTypeAdapter(ResponseObject.class, new ResponseObjectTypeAdapter()); s_gBuilder.registerTypeAdapter(String.class, new EncodedStringTypeAdapter()); - s_gBuilder.registerTypeAdapter(IdentityProxy.class, new IdentityTypeAdapter()); } public static GsonBuilder getBuilder() { diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 47754395e7a..edb798b812e 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -482,7 +482,7 @@ public class ApiResponseHelper implements ResponseGenerator { response.setEndPoint(result.getEndPoint()); response.setHttpsFlag(result.getHttpsFlag()); response.setMaxErrorRetry(result.getMaxErrorRetry()); - response.setObjectId(result.getId()); + response.setObjectId(result.getUuid()); response.setSecretKey(result.getSecretKey()); response.setSocketTimeout(result.getSocketTimeout()); response.setTemplateBucketName(result.getBucketName()); diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 519908daf25..17a2b29638b 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -400,12 +400,12 @@ public class ApiServer implements HttpRequestHandler { // BaseAsyncCmd: cmd is processed and submitted as an AsyncJob, job related info is serialized and returned. if (cmdObj instanceof BaseAsyncCmd) { Long objectId = null; - String objectEntityTable = null; + String objectUuid = null; if (cmdObj instanceof BaseAsyncCreateCmd) { BaseAsyncCreateCmd createCmd = (BaseAsyncCreateCmd) cmdObj; _dispatcher.dispatchCreateCmd(createCmd, params); objectId = createCmd.getEntityId(); - objectEntityTable = createCmd.getEntityTable(); + objectUuid = createCmd.getEntityUuid(); params.put("id", objectId.toString()); } else { ApiDispatcher.processParameters(cmdObj, params); @@ -449,8 +449,8 @@ public class ApiServer implements HttpRequestHandler { } if (objectId != null) { - SerializationContext.current().setUuidTranslation(true); - return ((BaseAsyncCreateCmd) asyncCmd).getResponse(jobId, objectId, objectEntityTable); + String objUuid = (objectUuid == null) ? objectId.toString() : objectUuid; + return ((BaseAsyncCreateCmd) asyncCmd).getResponse(jobId, objUuid); } SerializationContext.current().setUuidTranslation(true); @@ -460,6 +460,7 @@ public class ApiServer implements HttpRequestHandler { // if the command is of the listXXXCommand, we will need to also return the // the job id and status if possible + // For those listXXXCommand which we have already created DB views, this step is not needed since async job is joined in their db views. if (cmdObj instanceof BaseListCmd && !(cmdObj instanceof ListVMsCmd) && !(cmdObj instanceof ListRoutersCmd) && !(cmdObj instanceof ListSecurityGroupsCmd) && !(cmdObj instanceof ListTagsCmd) diff --git a/server/src/com/cloud/api/IdentityTypeAdapter.java b/server/src/com/cloud/api/IdentityTypeAdapter.java deleted file mode 100644 index 369c2020c24..00000000000 --- a/server/src/com/cloud/api/IdentityTypeAdapter.java +++ /dev/null @@ -1,80 +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. -package com.cloud.api; - -import java.lang.reflect.Type; - -import com.cloud.uuididentity.dao.IdentityDao; -import com.cloud.uuididentity.dao.IdentityDaoImpl; -import com.google.gson.Gson; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; -import com.cloud.utils.IdentityProxy; - - -public class IdentityTypeAdapter implements JsonSerializer, JsonDeserializer { - - @Override - public JsonElement serialize(IdentityProxy src, Type srcType, JsonSerializationContext context) { - if(SerializationContext.current().getUuidTranslation()) { - assert(src != null); - if(src.getValue() == null) - return context.serialize(null); - - IdentityDao identityDao = new IdentityDaoImpl(); - if(src.getTableName() != null) { - String uuid = identityDao.getIdentityUuid(src.getTableName(), String.valueOf(src.getValue())); - if(uuid == null) - return context.serialize(null); - - // Exceptions set the _idFieldName in the IdentityProxy structure. So if this field is not - // null, prepare a structure of uuid and idFieldName and return the json representation of that. - String idName = src.getidFieldName(); - if (idName != null) { - // Prepare a structure. - JsonObject jsonObj = new JsonObject(); - jsonObj.add("uuid", new JsonPrimitive(uuid)); - jsonObj.add("uuidProperty", new JsonPrimitive(idName)); - return jsonObj; - } - return new JsonPrimitive(uuid); - } else { - return new JsonPrimitive(String.valueOf(src.getValue())); - } - } else { - return new Gson().toJsonTree(src); - } - } - - @Override - public IdentityProxy deserialize(JsonElement src, Type srcType, - JsonDeserializationContext context) throws JsonParseException { - - IdentityProxy obj = new IdentityProxy(); - JsonObject json = src.getAsJsonObject(); - obj.setTableName(json.get("_tableName").getAsString()); - if(json.get("_value") != null) - obj.setValue(json.get("_value").getAsLong()); - return obj; - } -} diff --git a/server/src/com/cloud/api/response/ApiResponseSerializer.java b/server/src/com/cloud/api/response/ApiResponseSerializer.java index 470cc5f9587..11aee3d9390 100644 --- a/server/src/com/cloud/api/response/ApiResponseSerializer.java +++ b/server/src/com/cloud/api/response/ApiResponseSerializer.java @@ -37,7 +37,6 @@ import com.cloud.api.ApiResponseGsonHelper; import com.cloud.api.ApiServer; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.ResponseObject; -import com.cloud.utils.IdentityProxy; import com.cloud.utils.encoding.URLEncoder; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.uuididentity.dao.IdentityDao; @@ -226,27 +225,17 @@ public class ApiResponseSerializer { subObj.setObjectName(serializedName.value()); } serializeResponseObjXML(sb, subObj); - } else if (value instanceof IdentityProxy) { - // Only exception reponses carry a list of IdentityProxy objects. - IdentityProxy idProxy = (IdentityProxy)value; - String id = (idProxy.getValue() != null ? String.valueOf(idProxy.getValue()) : ""); - if(!id.isEmpty()) { - IdentityDao identityDao = new IdentityDaoImpl(); - id = identityDao.getIdentityUuid(idProxy.getTableName(), id); - } - if(id != null && !id.isEmpty()) { - // If this is the first IdentityProxy field encountered, put in a uuidList tag. - if (!usedUuidList) { - sb.append("<").append(serializedName.value()).append(">"); - usedUuidList = true; - } - sb.append("").append(id).append(""); - } - // Append the new idFieldName property also. - String idFieldName = idProxy.getidFieldName(); - if (idFieldName != null) { - sb.append("").append(idFieldName).append(""); - } + } else { + // Only exception reponses carry a list of uuid + // strings. + // If this is the first IdentityProxy field + // encountered, put in a uuidList tag. + if (!usedUuidList) { + sb.append("<").append(serializedName.value()).append(">"); + usedUuidList = true; + } + sb.append("").append(value).append(""); + // We have removed uuid property field due to removal of IdentityProxy class. } } if (usedUuidList) { @@ -256,19 +245,6 @@ public class ApiResponseSerializer { } else if (fieldValue instanceof Date) { sb.append("<").append(serializedName.value()).append(">").append(BaseCmd.getDateString((Date) fieldValue)). append(""); - } else if (fieldValue instanceof IdentityProxy) { - IdentityProxy idProxy = (IdentityProxy)fieldValue; - String id = (idProxy.getValue() != null ? String.valueOf(idProxy.getValue()) : ""); - if(!id.isEmpty()) { - IdentityDao identityDao = new IdentityDaoImpl(); - if(idProxy.getTableName() != null) { - id = identityDao.getIdentityUuid(idProxy.getTableName(), id); - } else { - s_logger.warn("IdentityProxy sanity check issue, invalid IdentityProxy table name found in class: " + obj.getClass().getName()); - } - } - if(id != null && !id.isEmpty()) - sb.append("<").append(serializedName.value()).append(">").append(id).append(""); } else { String resultString = escapeSpecialXmlChars(fieldValue.toString()); if (!(obj instanceof ExceptionResponse)) { diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index af5989cf003..b25c63f6d7f 100755 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -103,7 +103,6 @@ public class ConfigurationServerImpl implements ConfigurationServer { private final AccountDao _accountDao; private final ResourceCountDao _resourceCountDao; private final NetworkOfferingServiceMapDao _ntwkOfferingServiceMapDao; - private final IdentityDao _identityDao; public ConfigurationServerImpl() { ComponentLocator locator = ComponentLocator.getLocator(Name); @@ -120,7 +119,6 @@ public class ConfigurationServerImpl implements ConfigurationServer { _accountDao = locator.getDao(AccountDao.class); _resourceCountDao = locator.getDao(ResourceCountDao.class); _ntwkOfferingServiceMapDao = locator.getDao(NetworkOfferingServiceMapDao.class); - _identityDao = locator.getDao(IdentityDao.class); } @Override diff --git a/utils/src/com/cloud/utils/IdentityProxy.java b/utils/src/com/cloud/utils/IdentityProxy.java deleted file mode 100644 index 7e385fbf05a..00000000000 --- a/utils/src/com/cloud/utils/IdentityProxy.java +++ /dev/null @@ -1,60 +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 -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.utils; - -public class IdentityProxy { - private String _tableName; - private Long _value; - private String _idFieldName; - - public IdentityProxy() { - } - - public IdentityProxy(String tableName) { - _tableName = tableName; - } - - public IdentityProxy(String tableName, Long id, String fieldName) { - _tableName = tableName; - _value = id; - _idFieldName = fieldName; - } - - public String getTableName() { - return _tableName; - } - - public void setTableName(String tableName) { - _tableName = tableName; - } - - public Long getValue() { - return _value; - } - - public void setValue(Long value) { - _value = value; - } - - public void setidFieldName(String value) { - _idFieldName = value; - } - - public String getidFieldName() { - return _idFieldName; - } -} diff --git a/utils/src/com/cloud/utils/exception/RuntimeCloudException.java b/utils/src/com/cloud/utils/exception/RuntimeCloudException.java index 233469678df..52229800785 100644 --- a/utils/src/com/cloud/utils/exception/RuntimeCloudException.java +++ b/utils/src/com/cloud/utils/exception/RuntimeCloudException.java @@ -17,7 +17,6 @@ package com.cloud.utils.exception; import com.cloud.utils.AnnotationHelper; -import com.cloud.utils.IdentityProxy; import java.util.ArrayList; /** From cd7f7716155c3cd2d29b97d7ea302518d81fdfab Mon Sep 17 00:00:00 2001 From: Kishan Kavala Date: Thu, 10 Jan 2013 14:53:29 +0530 Subject: [PATCH 14/73] CLOUDSTACK-721: Fixed network usage. Send network usage command for isolated guest nic of non VPC VR. Send network usage command for public nic in VPC VR. --- .../VirtualNetworkApplianceManagerImpl.java | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 1a6eb0982a9..1f74c7174e8 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -823,26 +823,29 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian String privateIP = router.getPrivateIpAddress(); if (privateIP != null) { + boolean forVpc = router.getVpcId() != null; List routerNics = _nicDao.listByVmId(router.getId()); for (Nic routerNic : routerNics) { Network network = _networkMgr.getNetwork(routerNic.getNetworkId()); - if (network.getTrafficType() == TrafficType.Public) { - boolean forVpc = router.getVpcId() != null; + //Send network usage command for public nic in VPC VR + //Send network usage command for isolated guest nic of non VPC VR + if ((forVpc && network.getTrafficType() == TrafficType.Public) || (!forVpc && network.getTrafficType() == TrafficType.Guest && network.getGuestType() == Network.GuestType.Isolated)) { final NetworkUsageCommand usageCmd = new NetworkUsageCommand(privateIP, router.getHostName(), forVpc, routerNic.getIp4Address()); - UserStatisticsVO previousStats = _statsDao.findBy(router.getAccountId(), - router.getDataCenterIdToDeployIn(), network.getId(), null, router.getId(), router.getType().toString()); + String routerType = router.getType().toString(); + UserStatisticsVO previousStats = _statsDao.findBy(router.getAccountId(), + router.getDataCenterIdToDeployIn(), network.getId(), (forVpc ? routerNic.getIp4Address() : null), router.getId(), routerType); NetworkUsageAnswer answer = null; try { answer = (NetworkUsageAnswer) _agentMgr.easySend(router.getHostId(), usageCmd); } catch (Exception e) { - s_logger.warn("Error while collecting network stats from router: "+router.getInstanceName()+" from host: "+router.getHostId(), e); + s_logger.warn("Error while collecting network stats from router: " + router.getInstanceName() + " from host: " + router.getHostId(), e); continue; } if (answer != null) { if (!answer.getResult()) { - s_logger.warn("Error while collecting network stats from router: "+router.getInstanceName()+" from host: "+router.getHostId() + "; details: " + answer.getDetails()); + s_logger.warn("Error while collecting network stats from router: " + router.getInstanceName() + " from host: " + router.getHostId() + "; details: " + answer.getDetails()); continue; } Transaction txn = Transaction.open(Transaction.CLOUD_DB); @@ -852,27 +855,27 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian continue; } txn.start(); - UserStatisticsVO stats = _statsDao.lock(router.getAccountId(), - router.getDataCenterIdToDeployIn(), network.getId(), routerNic.getIp4Address(), router.getId(), router.getType().toString()); + UserStatisticsVO stats = _statsDao.lock(router.getAccountId(), + router.getDataCenterIdToDeployIn(), network.getId(), (forVpc ? routerNic.getIp4Address() : null), router.getId(), routerType); if (stats == null) { s_logger.warn("unable to find stats for account: " + router.getAccountId()); continue; } - if(previousStats != null - && ((previousStats.getCurrentBytesReceived() != stats.getCurrentBytesReceived()) - || (previousStats.getCurrentBytesSent() != stats.getCurrentBytesSent()))){ + if (previousStats != null + && ((previousStats.getCurrentBytesReceived() != stats.getCurrentBytesReceived()) + || (previousStats.getCurrentBytesSent() != stats.getCurrentBytesSent()))) { s_logger.debug("Router stats changed from the time NetworkUsageCommand was sent. " + - "Ignoring current answer. Router: "+answer.getRouterName()+" Rcvd: " + - answer.getBytesReceived()+ "Sent: " +answer.getBytesSent()); + "Ignoring current answer. Router: " + answer.getRouterName() + " Rcvd: " + + answer.getBytesReceived() + "Sent: " + answer.getBytesSent()); continue; } if (stats.getCurrentBytesReceived() > answer.getBytesReceived()) { if (s_logger.isDebugEnabled()) { s_logger.debug("Received # of bytes that's less than the last one. " + - "Assuming something went wrong and persisting it. Router: " + - answer.getRouterName()+" Reported: " + answer.getBytesReceived() + "Assuming something went wrong and persisting it. Router: " + + answer.getRouterName() + " Reported: " + answer.getBytesReceived() + " Stored: " + stats.getCurrentBytesReceived()); } stats.setNetBytesReceived(stats.getNetBytesReceived() + stats.getCurrentBytesReceived()); @@ -881,8 +884,8 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian if (stats.getCurrentBytesSent() > answer.getBytesSent()) { if (s_logger.isDebugEnabled()) { s_logger.debug("Received # of bytes that's less than the last one. " + - "Assuming something went wrong and persisting it. Router: " + - answer.getRouterName()+" Reported: " + answer.getBytesSent() + "Assuming something went wrong and persisting it. Router: " + + answer.getRouterName() + " Reported: " + answer.getBytesSent() + " Stored: " + stats.getCurrentBytesSent()); } stats.setNetBytesSent(stats.getNetBytesSent() + stats.getCurrentBytesSent()); From 1b8e17255f24a3e6aaf2265b1c3f8e27377eca76 Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Thu, 10 Jan 2013 20:29:12 +0530 Subject: [PATCH 15/73] integration test fix: test iso - obj reference within classmethod --- test/integration/smoke/test_iso.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/smoke/test_iso.py b/test/integration/smoke/test_iso.py index 22d424f86cc..8228a278cc9 100644 --- a/test/integration/smoke/test_iso.py +++ b/test/integration/smoke/test_iso.py @@ -219,14 +219,14 @@ class TestISO(cloudstackTestCase): # Finding the OsTypeId from Ostype ostypes = list_os_types( cls.api_client, - description=self.services["ostype"] + description=cls.services["ostype"] ) if not isinstance(ostypes, list): raise unittest.SkipTest("OSTypeId for given description not found") - self.services["iso_1"]["ostypeid"] = ostypes[0].id - self.services["iso_2"]["ostypeid"] = ostypes[0].id - self.services["ostypeid"] = ostypes[0].id + cls.services["iso_1"]["ostypeid"] = ostypes[0].id + cls.services["iso_2"]["ostypeid"] = ostypes[0].id + cls.services["ostypeid"] = ostypes[0].id cls.iso_1 = Iso.create( cls.api_client, From c6d9877d6445d36e2677c7b4ac7deec6a668a053 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 10:59:13 -0800 Subject: [PATCH 16/73] ApiDiscoveryService: Move refactor, interface should be in plugins and not in cloud-api Signed-off-by: Rohit Yadav --- .../src/org/apache/cloudstack/discovery/ApiDiscoveryService.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {api => plugins/api/discovery}/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java (100%) diff --git a/api/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java similarity index 100% rename from api/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java rename to plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java From f40e7b7511274742ac3531781b90124afbf02fc6 Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Thu, 10 Jan 2013 11:05:20 -0800 Subject: [PATCH 17/73] removed componentlocator and inject --- api/src/com/cloud/user/UserContext.java | 6 +- .../org/apache/cloudstack/api/BaseCmd.java | 84 +- .../admin/network/AddNetworkDeviceCmd.java | 18 +- .../admin/network/DeleteNetworkDeviceCmd.java | 17 +- .../admin/network/ListNetworkDeviceCmd.java | 18 +- .../auth/ec2/AuthenticationHandler.java | 239 +- .../bridge/auth/s3/AuthenticationHandler.java | 336 +- .../persist/dao/BucketPolicyDaoImpl.java | 57 +- .../dao/CloudStackConfigurationDaoImpl.java | 54 +- .../bridge/persist/dao/MultipartLoadDao.java | 418 +- .../bridge/persist/dao/SObjectDaoImpl.java | 87 +- .../cloud/bridge/service/EC2MainServlet.java | 119 +- .../cloud/bridge/service/EC2RestServlet.java | 2272 +++++----- .../cloud/bridge/service/S3RestServlet.java | 1218 +++--- .../service/controller/s3/S3BucketAction.java | 1914 +++++---- .../service/controller/s3/S3ObjectAction.java | 2213 +++++----- .../controller/s3/ServiceProvider.java | 572 ++- .../bridge/service/core/ec2/EC2Engine.java | 3699 ++++++++--------- .../bridge/service/core/s3/S3Engine.java | 2979 +++++++------ .../CifsSecondaryStorageResource.java | 569 ++- .../LocalSecondaryStorageResource.java | 56 +- .../resource/NfsSecondaryStorageResource.java | 809 ++-- .../storage/template/DownloadManagerImpl.java | 235 +- .../entity/api/db/dao/ClusterDaoImpl.java | 174 +- .../entity/api/db/dao/DataCenterDaoImpl.java | 151 +- .../entity/api/db/dao/HostDaoImpl.java | 308 +- .../DefaultImageDataStoreProvider.java | 8 +- .../datastore/DefaultPrimaryDataStore.java | 38 +- .../configurator/kvm/KvmNfsConfigurator.java | 4 +- .../vmware/VmwareNfsConfigurator.java | 5 +- .../configurator/xen/XenNfsConfigurator.java | 20 +- .../datastore/db/PrimaryDataStoreDaoImpl.java | 9 +- .../storage/volume/VolumeManagerImpl.java | 62 +- .../storage/volume/VolumeObject.java | 18 +- .../acl/StaticRoleBasedAPIAccessChecker.java | 6 +- pom.xml | 2 +- server/pom.xml | 10 + .../cloud/agent/manager/AgentManagerImpl.java | 315 +- .../manager/ClusteredAgentManagerImpl.java | 166 +- .../allocator/impl/TestingAllocator.java | 20 +- .../src/com/cloud/alert/AlertManagerImpl.java | 491 ++- .../cloud/alert/ConsoleProxyAlertAdapter.java | 337 +- server/src/com/cloud/api/ApiDBUtils.java | 348 +- server/src/com/cloud/api/ApiDispatcher.java | 328 +- .../com/cloud/api/query/QueryManagerImpl.java | 36 +- .../baremetal/BareMetalVmManagerImpl.java | 4 +- .../cloud/baremetal/PxeServerManagerImpl.java | 4 +- .../ConfigurationManagerImpl.java | 176 +- .../com/cloud/network/NetworkManagerImpl.java | 38 +- .../cloud/servlet/CloudStartupServlet.java | 4 +- .../cloud/template/TemplateManagerImpl.java | 6 +- .../com/cloud/async/TestAsyncJobManager.java | 317 +- .../com/cloud/async/TestSyncQueueManager.java | 335 +- .../cloud/cluster/CheckPointManagerTest.java | 390 -- .../cloud/utils/component/AdapterBase.java | 9 + .../com/cloud/utils/component/Adapters.java | 93 - .../utils/component/ComponentContext.java | 4 +- .../utils/component/ComponentInject.java | 27 - .../utils/component/ComponentLibrary.java | 56 - .../utils/component/ComponentLibraryBase.java | 99 - .../utils/component/ComponentLocator.java | 58 - .../component/ComponentLocatorMBean.java | 43 - .../src/com/cloud/utils/component/Inject.java | 30 - .../component/LegacyComponentLocator.java | 1282 ------ .../src/com/cloud/utils/component/Plugin.java | 64 - .../utils/component/MockComponentLocator.java | 121 - .../cloud/utils/testcase/ComponentSetup.java | 28 - .../utils/testcase/ComponentTestCase.java | 44 - 68 files changed, 10782 insertions(+), 13295 deletions(-) delete mode 100755 server/test/com/cloud/cluster/CheckPointManagerTest.java delete mode 100755 utils/src/com/cloud/utils/component/Adapters.java delete mode 100644 utils/src/com/cloud/utils/component/ComponentInject.java delete mode 100755 utils/src/com/cloud/utils/component/ComponentLibrary.java delete mode 100644 utils/src/com/cloud/utils/component/ComponentLibraryBase.java delete mode 100644 utils/src/com/cloud/utils/component/ComponentLocator.java delete mode 100755 utils/src/com/cloud/utils/component/ComponentLocatorMBean.java delete mode 100644 utils/src/com/cloud/utils/component/Inject.java delete mode 100755 utils/src/com/cloud/utils/component/LegacyComponentLocator.java delete mode 100755 utils/src/com/cloud/utils/component/Plugin.java delete mode 100755 utils/test/com/cloud/utils/component/MockComponentLocator.java delete mode 100644 utils/test/com/cloud/utils/testcase/ComponentSetup.java delete mode 100644 utils/test/com/cloud/utils/testcase/ComponentTestCase.java diff --git a/api/src/com/cloud/user/UserContext.java b/api/src/com/cloud/user/UserContext.java index 786590eed09..39da4eafd2e 100644 --- a/api/src/com/cloud/user/UserContext.java +++ b/api/src/com/cloud/user/UserContext.java @@ -16,13 +16,11 @@ // under the License. package com.cloud.user; -import com.cloud.server.ManagementService; -import com.cloud.utils.component.ComponentLocator; public class UserContext { private static ThreadLocal s_currentContext = new ThreadLocal(); - + private long userId; private String sessionId; private Account account; @@ -82,7 +80,7 @@ public class UserContext { // however, there are many places that run background jobs assume the system context. // // If there is a security concern, all entry points from user (including the front end that takes HTTP - // request in and + // request in and // the core async-job manager that runs commands from user) have explicitly setup the UserContext. // return UserContextInitializer.getInstance().getAdminContext(); diff --git a/api/src/org/apache/cloudstack/api/BaseCmd.java b/api/src/org/apache/cloudstack/api/BaseCmd.java index fbbee50b578..c0ef8a0985e 100644 --- a/api/src/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseCmd.java @@ -19,7 +19,6 @@ package org.apache.cloudstack.api; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -64,10 +63,8 @@ import com.cloud.user.Account; import com.cloud.user.AccountService; import com.cloud.user.DomainService; import com.cloud.user.ResourceLimitService; -import com.cloud.utils.IdentityProxy; import com.cloud.utils.Pair; import com.cloud.utils.component.ComponentContext; -import com.cloud.utils.component.ComponentLocator; import com.cloud.vm.BareMetalVmService; import com.cloud.vm.UserVmService; @@ -112,7 +109,6 @@ public abstract class BaseCmd { @Parameter(name = "response", type = CommandType.STRING) private String responseType; - public static ComponentLocator s_locator; public static ConfigurationService _configService; public static AccountService _accountService; public static UserVmService _userVmService; @@ -145,38 +141,48 @@ public abstract class BaseCmd { public static QueryService _queryService; - public static void setComponents(ResponseGenerator generator) { - ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); - _mgr = (ManagementService) ComponentLocator.getComponent(ManagementService.Name); - _accountService = locator.getManager(AccountService.class); - _configService = locator.getManager(ConfigurationService.class); - _userVmService = locator.getManager(UserVmService.class); - _storageService = locator.getManager(StorageService.class); - _resourceService = locator.getManager(ResourceService.class); - _networkService = locator.getManager(NetworkService.class); - _templateService = locator.getManager(TemplateService.class); - _securityGroupService = locator.getManager(SecurityGroupService.class); - _snapshotService = locator.getManager(SnapshotService.class); - _consoleProxyService = locator.getManager(ConsoleProxyService.class); - _routerService = locator.getManager(VpcVirtualNetworkApplianceService.class); - _entityMgr = locator.getManager(EntityManager.class); - _rulesService = locator.getManager(RulesService.class); - _lbService = locator.getManager(LoadBalancingRulesService.class); - _autoScaleService = locator.getManager(AutoScaleService.class); - _ravService = locator.getManager(RemoteAccessVpnService.class); + static void setComponents(ResponseGenerator generator) { + _mgr = ComponentContext.getComponent(ManagementService.class); + _accountService = ComponentContext.getComponent(AccountService.class); + _configService = ComponentContext.getComponent(ConfigurationService.class); + + _userVmService = ComponentContext.getComponent(UserVmService.class); + + // TODO, ugly and will change soon + // + Map svmServices = ComponentContext.getComponentsOfType(UserVmService.class); + _userVmService = svmServices.get("BareMetalVmManagerImpl"); + + _storageService = ComponentContext.getComponent(StorageService.class); + _resourceService = ComponentContext.getComponent(ResourceService.class); + + _networkService = ComponentContext.getComponent(NetworkService.class); + _templateService = ComponentContext.getComponent(TemplateService.class); + + // TODO, will change to looking for primary component + // ugly binding to a specific implementation + Map _sgServices = ComponentContext.getComponentsOfType(SecurityGroupService.class); + _securityGroupService = _sgServices.get("SecurityGroupManagerImpl2"); + + _snapshotService = ComponentContext.getComponent(SnapshotService.class); + _consoleProxyService = ComponentContext.getComponent(ConsoleProxyService.class); + _routerService = ComponentContext.getComponent(VpcVirtualNetworkApplianceService.class); + _entityMgr = ComponentContext.getComponent(EntityManager.class); + _rulesService = ComponentContext.getComponent(RulesService.class); + _lbService = ComponentContext.getComponent(LoadBalancingRulesService.class); + _ravService = ComponentContext.getComponent(RemoteAccessVpnService.class); _responseGenerator = generator; - _bareMetalVmService = locator.getManager(BareMetalVmService.class); - _projectService = locator.getManager(ProjectService.class); - _firewallService = locator.getManager(FirewallService.class); - _domainService = locator.getManager(DomainService.class); - _resourceLimitService = locator.getManager(ResourceLimitService.class); - _identityService = locator.getManager(IdentityService.class); - _storageNetworkService = locator.getManager(StorageNetworkService.class); - _taggedResourceService = locator.getManager(TaggedResourceService.class); - _vpcService = locator.getManager(VpcService.class); - _networkACLService = locator.getManager(NetworkACLService.class); - _s2sVpnService = locator.getManager(Site2SiteVpnService.class); - _queryService = locator.getManager(QueryService.class); + _bareMetalVmService = ComponentContext.getComponent(BareMetalVmService.class); + _projectService = ComponentContext.getComponent(ProjectService.class); + _firewallService = ComponentContext.getComponent(FirewallService.class); + _domainService = ComponentContext.getComponent(DomainService.class); + _resourceLimitService = ComponentContext.getComponent(ResourceLimitService.class); + _identityService = ComponentContext.getComponent(IdentityService.class); + _storageNetworkService = ComponentContext.getComponent(StorageNetworkService.class); + _taggedResourceService = ComponentContext.getComponent(TaggedResourceService.class); + _vpcService = ComponentContext.getComponent(VpcService.class); + _networkACLService = ComponentContext.getComponent(NetworkACLService.class); + _s2sVpnService = ComponentContext.getComponent(Site2SiteVpnService.class); } public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException; @@ -211,9 +217,9 @@ public abstract class BaseCmd { } public ManagementService getMgmtServiceRef() { - return _mgr; + return _mgr; } - + public static String getDateString(Date date) { if (date == null) { return ""; @@ -526,8 +532,8 @@ public abstract class BaseCmd { if (!enabledOnly || project.getState() == Project.State.Active) { return project.getProjectAccountId(); } else { - PermissionDeniedException ex = new PermissionDeniedException("Can't add resources to the project with specified projectId in state=" + project.getState() + " as it's no longer active"); - ex.addProxyObject(project, projectId, "projectId"); + PermissionDeniedException ex = new PermissionDeniedException("Can't add resources to the project with specified projectId in state=" + project.getState() + " as it's no longer active"); + ex.addProxyObject(project, projectId, "projectId"); throw ex; } } else { diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java index 3e1d74df405..793c982d0f1 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java @@ -18,23 +18,23 @@ package org.apache.cloudstack.api.command.admin.network; import java.util.Map; -import org.apache.log4j.Logger; +import javax.inject.Inject; +import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.NetworkDeviceResponse; +import org.apache.cloudstack.network.ExternalNetworkDeviceManager; +import org.apache.log4j.Logger; + import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.host.Host; -import org.apache.cloudstack.network.ExternalNetworkDeviceManager; -import com.cloud.server.ManagementService; -import org.apache.cloudstack.api.response.NetworkDeviceResponse; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.exception.CloudRuntimeException; @APICommand(name = "addNetworkDevice", description="Adds a network device of one of the following types: ExternalDhcp, ExternalFirewall, ExternalLoadBalancer, PxeServer", responseObject = NetworkDeviceResponse.class) @@ -46,6 +46,7 @@ public class AddNetworkDeviceCmd extends BaseCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// + @Inject ExternalNetworkDeviceManager nwDeviceMgr; @Parameter(name = ApiConstants.NETWORK_DEVICE_TYPE, type = CommandType.STRING, description = "Network device type, now supports ExternalDhcp, PxeServer, NetscalerMPXLoadBalancer, NetscalerVPXLoadBalancer, NetscalerSDXLoadBalancer, F5BigIpLoadBalancer, JuniperSRXFirewall") private String type; @@ -63,11 +64,8 @@ public class AddNetworkDeviceCmd extends BaseCmd { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, - ResourceAllocationException { + ResourceAllocationException { try { - ExternalNetworkDeviceManager nwDeviceMgr; - ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); - nwDeviceMgr = locator.getManager(ExternalNetworkDeviceManager.class); Host device = nwDeviceMgr.addNetworkDevice(this); NetworkDeviceResponse response = nwDeviceMgr.getApiResponse(device); response.setObjectName("networkdevice"); diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/DeleteNetworkDeviceCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/DeleteNetworkDeviceCmd.java index 09451242daf..38971c1f3a9 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/network/DeleteNetworkDeviceCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/network/DeleteNetworkDeviceCmd.java @@ -16,23 +16,23 @@ // under the License. package org.apache.cloudstack.api.command.admin.network; -import org.apache.log4j.Logger; +import javax.inject.Inject; +import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.network.ExternalNetworkDeviceManager; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.network.ExternalNetworkDeviceManager; +import org.apache.log4j.Logger; + import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.server.ManagementService; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.exception.CloudRuntimeException; @APICommand(name = "deleteNetworkDevice", description="Deletes network device.", responseObject=SuccessResponse.class) @@ -40,6 +40,8 @@ public class DeleteNetworkDeviceCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(DeleteNetworkDeviceCmd.class); private static final String s_name = "deletenetworkdeviceresponse"; + @Inject ExternalNetworkDeviceManager nwDeviceMgr; + ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @@ -54,11 +56,8 @@ public class DeleteNetworkDeviceCmd extends BaseCmd { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, - ResourceAllocationException { + ResourceAllocationException { try { - ExternalNetworkDeviceManager nwDeviceMgr; - ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); - nwDeviceMgr = locator.getManager(ExternalNetworkDeviceManager.class); boolean result = nwDeviceMgr.deleteNetworkDevice(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java index 742ff1f74af..04a26af0781 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java @@ -20,25 +20,25 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import org.apache.log4j.Logger; +import javax.inject.Inject; +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.APICommand; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.network.ExternalNetworkDeviceManager; -import org.apache.cloudstack.api.response.NetworkDeviceResponse; import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkDeviceResponse; +import org.apache.cloudstack.network.ExternalNetworkDeviceManager; +import org.apache.log4j.Logger; + import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.host.Host; -import com.cloud.server.ManagementService; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.exception.CloudRuntimeException; @APICommand(name = "listNetworkDevice", description="List network devices", responseObject = NetworkDeviceResponse.class) @@ -46,6 +46,7 @@ public class ListNetworkDeviceCmd extends BaseListCmd { public static final Logger s_logger = Logger.getLogger(ListNetworkDeviceCmd.class); private static final String s_name = "listnetworkdevice"; + @Inject ExternalNetworkDeviceManager nwDeviceMgr; ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @@ -66,11 +67,8 @@ public class ListNetworkDeviceCmd extends BaseListCmd { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, - ResourceAllocationException { + ResourceAllocationException { try { - ExternalNetworkDeviceManager nwDeviceMgr; - ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); - nwDeviceMgr = locator.getManager(ExternalNetworkDeviceManager.class); List devices = nwDeviceMgr.listNetworkDevice(this); List nwdeviceResponses = new ArrayList(); ListResponse listResponse = new ListResponse(); diff --git a/awsapi/src/com/cloud/bridge/auth/ec2/AuthenticationHandler.java b/awsapi/src/com/cloud/bridge/auth/ec2/AuthenticationHandler.java index f79feaad5bc..04060844697 100644 --- a/awsapi/src/com/cloud/bridge/auth/ec2/AuthenticationHandler.java +++ b/awsapi/src/com/cloud/bridge/auth/ec2/AuthenticationHandler.java @@ -16,142 +16,147 @@ // under the License. package com.cloud.bridge.auth.ec2; -import org.apache.axiom.soap.SOAPEnvelope; -import org.apache.log4j.Logger; -import org.apache.axis2.context.MessageContext; -import org.apache.axis2.engine.Handler; -import org.apache.axis2.AxisFault; -import org.apache.axis2.description.HandlerDescription; -import org.apache.axis2.description.Parameter; -import org.apache.commons.codec.binary.Base64; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import javax.inject.Inject; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.apache.axiom.soap.SOAPEnvelope; +import org.apache.axis2.AxisFault; +import org.apache.axis2.context.MessageContext; +import org.apache.axis2.description.HandlerDescription; +import org.apache.axis2.description.Parameter; +import org.apache.axis2.engine.Handler; +import org.apache.commons.codec.binary.Base64; +import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import java.security.cert.Certificate; -import java.security.cert.CertificateFactory; -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - import com.cloud.bridge.model.UserCredentialsVO; -import com.cloud.bridge.persist.dao.UserCredentialsDaoImpl; +import com.cloud.bridge.persist.dao.UserCredentialsDao; import com.cloud.bridge.service.UserContext; import com.cloud.bridge.util.AuthenticationUtils; -import com.cloud.utils.component.ComponentLocator; public class AuthenticationHandler implements Handler { - protected final static Logger logger = Logger.getLogger(AuthenticationHandler.class); - protected final UserCredentialsDaoImpl ucDao = ComponentLocator.inject(UserCredentialsDaoImpl.class); - private DocumentBuilderFactory dbf = null; + protected final static Logger logger = Logger.getLogger(AuthenticationHandler.class); + @Inject protected UserCredentialsDao ucDao; + private DocumentBuilderFactory dbf = null; - protected HandlerDescription handlerDesc = new HandlerDescription( "EC2AuthenticationHandler" ); - private String name = "EC2AuthenticationHandler"; - - public void init( HandlerDescription handlerdesc ) - { - dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware( true ); + protected HandlerDescription handlerDesc = new HandlerDescription( "EC2AuthenticationHandler" ); + private String name = "EC2AuthenticationHandler"; - this.handlerDesc = handlerdesc; - } - - public String getName() - { - return name; - } + @Override + public void init( HandlerDescription handlerdesc ) + { + dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware( true ); - public String toString() - { - return (name != null) ? name.toString() : null; - } - - public HandlerDescription getHandlerDesc() - { - return handlerDesc; - } - - public Parameter getParameter( String name ) - { - return handlerDesc.getParameter( name ); - } - - - /** - * For EC2 SOAP calls this function's goal is to extract the X509 certificate that is - * part of the WS-Security wrapped SOAP request. We need the cert in order to - * map it to the user's Cloud API key and Cloud Secret Key. - */ - public InvocationResponse invoke(MessageContext msgContext) throws AxisFault - { - // -> the certificate we want is embedded into the soap header - try - { SOAPEnvelope soapEnvelope = msgContext.getEnvelope(); - String xmlHeader = soapEnvelope.toString(); - //System.out.println( "entire request: " + xmlHeader ); - - InputStream is = new ByteArrayInputStream( xmlHeader.getBytes("UTF-8")); - DocumentBuilder db = dbf.newDocumentBuilder(); - Document request = db.parse( is ); - NodeList certs = request.getElementsByTagNameNS( "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "BinarySecurityToken" ); - if (0 < certs.getLength()) { - Node item = certs.item(0); - String result = new String( item.getFirstChild().getNodeValue()); - byte[] certBytes = Base64.decodeBase64( result.getBytes()); + this.handlerDesc = handlerdesc; + } - Certificate userCert = null; - CertificateFactory cf = CertificateFactory.getInstance( "X.509" ); - ByteArrayInputStream bs = new ByteArrayInputStream( certBytes ); - while (bs.available() > 0) userCert = cf.generateCertificate(bs); - //System.out.println( "cert: " + userCert.toString()); - String uniqueId = AuthenticationUtils.X509CertUniqueId( userCert ); - logger.debug( "X509 cert's uniqueId: " + uniqueId ); - - // -> find the Cloud API key and the secret key from the cert's uniqueId -/* UserCredentialsDao credentialDao = new UserCredentialsDao(); + @Override + public String getName() + { + return name; + } + + @Override + public String toString() + { + return (name != null) ? name.toString() : null; + } + + @Override + public HandlerDescription getHandlerDesc() + { + return handlerDesc; + } + + @Override + public Parameter getParameter( String name ) + { + return handlerDesc.getParameter( name ); + } + + + /** + * For EC2 SOAP calls this function's goal is to extract the X509 certificate that is + * part of the WS-Security wrapped SOAP request. We need the cert in order to + * map it to the user's Cloud API key and Cloud Secret Key. + */ + @Override + public InvocationResponse invoke(MessageContext msgContext) throws AxisFault + { + // -> the certificate we want is embedded into the soap header + try + { SOAPEnvelope soapEnvelope = msgContext.getEnvelope(); + String xmlHeader = soapEnvelope.toString(); + //System.out.println( "entire request: " + xmlHeader ); + + InputStream is = new ByteArrayInputStream( xmlHeader.getBytes("UTF-8")); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document request = db.parse( is ); + NodeList certs = request.getElementsByTagNameNS( "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "BinarySecurityToken" ); + if (0 < certs.getLength()) { + Node item = certs.item(0); + String result = new String( item.getFirstChild().getNodeValue()); + byte[] certBytes = Base64.decodeBase64( result.getBytes()); + + Certificate userCert = null; + CertificateFactory cf = CertificateFactory.getInstance( "X.509" ); + ByteArrayInputStream bs = new ByteArrayInputStream( certBytes ); + while (bs.available() > 0) userCert = cf.generateCertificate(bs); + //System.out.println( "cert: " + userCert.toString()); + String uniqueId = AuthenticationUtils.X509CertUniqueId( userCert ); + logger.debug( "X509 cert's uniqueId: " + uniqueId ); + + // -> find the Cloud API key and the secret key from the cert's uniqueId + /* UserCredentialsDao credentialDao = new UserCredentialsDao(); UserCredentials cloudKeys = credentialDao.getByCertUniqueId( uniqueId ); -*/ - UserCredentialsVO cloudKeys = ucDao.getByCertUniqueId(uniqueId); - if ( null == cloudKeys ) { - logger.error( "Cert does not map to Cloud API keys: " + uniqueId ); - throw new AxisFault( "User not properly registered: Certificate does not map to Cloud API Keys", "Client.Blocked" ); - } - else UserContext.current().initContext( cloudKeys.getAccessKey(), cloudKeys.getSecretKey(), cloudKeys.getAccessKey(), "SOAP Request", null ); - //System.out.println( "end of cert match: " + UserContext.current().getSecretKey()); - } - } - catch (AxisFault e) { - throw e; - } - catch( Exception e ) { + */ + UserCredentialsVO cloudKeys = ucDao.getByCertUniqueId(uniqueId); + if ( null == cloudKeys ) { + logger.error( "Cert does not map to Cloud API keys: " + uniqueId ); + throw new AxisFault( "User not properly registered: Certificate does not map to Cloud API Keys", "Client.Blocked" ); + } + else UserContext.current().initContext( cloudKeys.getAccessKey(), cloudKeys.getSecretKey(), cloudKeys.getAccessKey(), "SOAP Request", null ); + //System.out.println( "end of cert match: " + UserContext.current().getSecretKey()); + } + } + catch (AxisFault e) { + throw e; + } + catch( Exception e ) { logger.error("EC2 Authentication Handler: ", e); - throw new AxisFault( "An unknown error occurred.", "Server.InternalError" ); - } + throw new AxisFault( "An unknown error occurred.", "Server.InternalError" ); + } return InvocationResponse.CONTINUE; - } + } - - public void revoke(MessageContext msgContext) - { - logger.info(msgContext.getEnvelope().toString()); - } - public void setName(String name) - { - this.name = name; - } - - @Override - public void cleanup() - { - } + public void revoke(MessageContext msgContext) + { + logger.info(msgContext.getEnvelope().toString()); + } - @Override - public void flowComplete( MessageContext arg0 ) - { - } + public void setName(String name) + { + this.name = name; + } + + @Override + public void cleanup() + { + } + + @Override + public void flowComplete( MessageContext arg0 ) + { + } } diff --git a/awsapi/src/com/cloud/bridge/auth/s3/AuthenticationHandler.java b/awsapi/src/com/cloud/bridge/auth/s3/AuthenticationHandler.java index b9519169632..5b20b02d0b3 100644 --- a/awsapi/src/com/cloud/bridge/auth/s3/AuthenticationHandler.java +++ b/awsapi/src/com/cloud/bridge/auth/s3/AuthenticationHandler.java @@ -18,197 +18,203 @@ package com.cloud.bridge.auth.s3; import java.sql.SQLException; +import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; -import org.apache.axiom.soap.SOAPEnvelope; import org.apache.axiom.soap.SOAPBody; -import org.apache.log4j.Logger; -import org.apache.axis2.context.MessageContext; -import org.apache.axis2.engine.Handler; +import org.apache.axiom.soap.SOAPEnvelope; import org.apache.axis2.AxisFault; -import org.apache.axis2.description.HandlerDescription; +import org.apache.axis2.context.MessageContext; +import org.apache.axis2.description.HandlerDescription; import org.apache.axis2.description.Parameter; +import org.apache.axis2.engine.Handler; +import org.apache.log4j.Logger; import com.cloud.bridge.model.UserCredentialsVO; import com.cloud.bridge.persist.dao.UserCredentialsDaoImpl; import com.cloud.bridge.service.UserContext; import com.cloud.bridge.util.S3SoapAuth; -import com.cloud.utils.component.ComponentLocator; /* * For SOAP compatibility. */ public class AuthenticationHandler implements Handler { - protected final static Logger logger = Logger.getLogger(AuthenticationHandler.class); - protected final UserCredentialsDaoImpl ucDao = ComponentLocator.inject(UserCredentialsDaoImpl.class); - protected HandlerDescription handlerDesc = new HandlerDescription( "default handler" ); - private String name = "S3AuthenticationHandler"; - - public void init( HandlerDescription handlerdesc ) - { - this.handlerDesc = handlerdesc; - } - - public String getName() - { - //logger.debug( "getName entry S3AuthenticationHandler" + name ); - return name; - } + protected final static Logger logger = Logger.getLogger(AuthenticationHandler.class); + @Inject UserCredentialsDaoImpl ucDao; + protected HandlerDescription handlerDesc = new HandlerDescription( "default handler" ); + private String name = "S3AuthenticationHandler"; - public String toString() - { - return (name != null) ? name.toString() : null; - } - - public HandlerDescription getHandlerDesc() - { - return handlerDesc; - } - - public Parameter getParameter( String name ) - { - return handlerDesc.getParameter( name ); - } - - - /** - * Verify the request's authentication signature by extracting all the - * necessary parts of the request, obtaining the requestor's secret key, and - * recalculating the signature. - * - * On Signature mismatch raise an AxisFault (i.e., a SoapFault) with what Amazon S3 - * defines as a "Client.SignatureMismatch" error. - * - * Special case: need to deal with anonymous requests where no AWSAccessKeyId is - * given. In this case just pass the request on. - */ - public InvocationResponse invoke(MessageContext msgContext) throws AxisFault - { - String accessKey = null; - String operation = null; - String msgSig = null; - String timestamp = null; - String secretKey = null; - String temp = null; - - // [A] Obtain the HttpServletRequest object - HttpServletRequest httpObj =(HttpServletRequest)msgContext.getProperty("transport.http.servletRequest"); - if (null != httpObj) System.out.println("S3 SOAP auth test header access - acceptable Encoding type: "+ httpObj.getHeader("Accept-Encoding")); - - // [A] Try to recalculate the signature for non-anonymous requests - try - { SOAPEnvelope soapEnvelope = msgContext.getEnvelope(); - SOAPBody soapBody = soapEnvelope.getBody(); - String xmlBody = soapBody.toString(); - //logger.debug( "xmlrequest: " + xmlBody ); - - // -> did we get here yet its an EC2 request? - int offset = xmlBody.indexOf( "http://ec2.amazonaws.com" ); - if (-1 != offset) return InvocationResponse.CONTINUE; - - - // -> if it is anonymous request, then no access key should exist - int start = xmlBody.indexOf( "AWSAccessKeyId>" ); - if (-1 == start) { - UserContext.current().initContext(); - return InvocationResponse.CONTINUE; - } - temp = xmlBody.substring( start+15 ); - int end = temp.indexOf( " what if we cannot find the user's key? - if (null != (secretKey = lookupSecretKey( accessKey ))) + @Override + public void init( HandlerDescription handlerdesc ) + { + this.handlerDesc = handlerdesc; + } + + @Override + public String getName() + { + //logger.debug( "getName entry S3AuthenticationHandler" + name ); + return name; + } + + @Override + public String toString() + { + return (name != null) ? name.toString() : null; + } + + @Override + public HandlerDescription getHandlerDesc() + { + return handlerDesc; + } + + @Override + public Parameter getParameter( String name ) + { + return handlerDesc.getParameter( name ); + } + + + /** + * Verify the request's authentication signature by extracting all the + * necessary parts of the request, obtaining the requestor's secret key, and + * recalculating the signature. + * + * On Signature mismatch raise an AxisFault (i.e., a SoapFault) with what Amazon S3 + * defines as a "Client.SignatureMismatch" error. + * + * Special case: need to deal with anonymous requests where no AWSAccessKeyId is + * given. In this case just pass the request on. + */ + @Override + public InvocationResponse invoke(MessageContext msgContext) throws AxisFault + { + String accessKey = null; + String operation = null; + String msgSig = null; + String timestamp = null; + String secretKey = null; + String temp = null; + + // [A] Obtain the HttpServletRequest object + HttpServletRequest httpObj =(HttpServletRequest)msgContext.getProperty("transport.http.servletRequest"); + if (null != httpObj) System.out.println("S3 SOAP auth test header access - acceptable Encoding type: "+ httpObj.getHeader("Accept-Encoding")); + + // [A] Try to recalculate the signature for non-anonymous requests + try + { SOAPEnvelope soapEnvelope = msgContext.getEnvelope(); + SOAPBody soapBody = soapEnvelope.getBody(); + String xmlBody = soapBody.toString(); + //logger.debug( "xmlrequest: " + xmlBody ); + + // -> did we get here yet its an EC2 request? + int offset = xmlBody.indexOf( "http://ec2.amazonaws.com" ); + if (-1 != offset) return InvocationResponse.CONTINUE; + + + // -> if it is anonymous request, then no access key should exist + int start = xmlBody.indexOf( "AWSAccessKeyId>" ); + if (-1 == start) { + UserContext.current().initContext(); + return InvocationResponse.CONTINUE; + } + temp = xmlBody.substring( start+15 ); + int end = temp.indexOf( " what if we cannot find the user's key? + if (null != (secretKey = lookupSecretKey( accessKey ))) + { + // -> if any other field is missing, then the signature will not match + if ( null != (operation = soapBody.getFirstElementLocalName())) + operation = operation.trim(); + else operation = ""; + //logger.debug( "operation " + operation ); + + start = xmlBody.indexOf( "Timestamp>" ); + if ( -1 < start ) { - // -> if any other field is missing, then the signature will not match - if ( null != (operation = soapBody.getFirstElementLocalName())) - operation = operation.trim(); - else operation = ""; - //logger.debug( "operation " + operation ); - - start = xmlBody.indexOf( "Timestamp>" ); - if ( -1 < start ) - { - temp = xmlBody.substring( start+10 ); - end = temp.indexOf( "" ); - if ( -1 < start ) - { - temp = xmlBody.substring( start+10 ); - end = temp.indexOf( "" ); + if ( -1 < start ) + { + temp = xmlBody.substring( start+10 ); + end = temp.indexOf( " for SOAP requests the Cloud API keys are sent here and only here - S3SoapAuth.verifySignature( msgSig, operation, timestamp, accessKey, secretKey ); + S3SoapAuth.verifySignature( msgSig, operation, timestamp, accessKey, secretKey ); UserContext.current().initContext( accessKey, secretKey, accessKey, "S3 SOAP request", httpObj ); return InvocationResponse.CONTINUE; - } + } - - public void revoke(MessageContext msgContext) - { - logger.info(msgContext.getEnvelope().toString()); - } - public void setName(String name) - { - //logger.debug( "setName entry S3AuthenticationHandler " + name ); - this.name = name; - } - - /** - * Given the user's access key, then obtain his secret key in the user database. - * - * @param accessKey - a unique string allocated for each registered user - * @return the secret key or null of no matching user found - */ - private String lookupSecretKey( String accessKey ) - throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException - { - UserCredentialsVO cloudKeys = ucDao.getByAccessKey( accessKey ); - if ( null == cloudKeys ) { - logger.debug( accessKey + " is not defined in the S3 service - call SetUserKeys" ); - return null; - } - else return cloudKeys.getSecretKey(); - } + public void revoke(MessageContext msgContext) + { + logger.info(msgContext.getEnvelope().toString()); + } - @Override - public void cleanup() - { - //logger.debug( "cleanup entry S3AuthenticationHandler " ); - } + public void setName(String name) + { + //logger.debug( "setName entry S3AuthenticationHandler " + name ); + this.name = name; + } - @Override - public void flowComplete( MessageContext arg0 ) - { - //logger.debug( "flowComplete entry S3AuthenticationHandler " ); - } + /** + * Given the user's access key, then obtain his secret key in the user database. + * + * @param accessKey - a unique string allocated for each registered user + * @return the secret key or null of no matching user found + */ + private String lookupSecretKey( String accessKey ) + throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException + { + UserCredentialsVO cloudKeys = ucDao.getByAccessKey( accessKey ); + if ( null == cloudKeys ) { + logger.debug( accessKey + " is not defined in the S3 service - call SetUserKeys" ); + return null; + } + else return cloudKeys.getSecretKey(); + } + + @Override + public void cleanup() + { + //logger.debug( "cleanup entry S3AuthenticationHandler " ); + } + + @Override + public void flowComplete( MessageContext arg0 ) + { + //logger.debug( "flowComplete entry S3AuthenticationHandler " ); + } } diff --git a/awsapi/src/com/cloud/bridge/persist/dao/BucketPolicyDaoImpl.java b/awsapi/src/com/cloud/bridge/persist/dao/BucketPolicyDaoImpl.java index ce230c3d57a..dd354a39ffb 100644 --- a/awsapi/src/com/cloud/bridge/persist/dao/BucketPolicyDaoImpl.java +++ b/awsapi/src/com/cloud/bridge/persist/dao/BucketPolicyDaoImpl.java @@ -23,7 +23,6 @@ import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.bridge.model.BucketPolicyVO; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -33,43 +32,43 @@ import com.cloud.utils.db.Transaction; @Local(value={BucketPolicyDao.class}) public class BucketPolicyDaoImpl extends GenericDaoBase implements BucketPolicyDao{ public static final Logger logger = Logger.getLogger(BucketPolicyDaoImpl.class); - public BucketPolicyDaoImpl(){ } + public BucketPolicyDaoImpl(){ } - /** - * Since a bucket policy can exist before its bucket we also need to keep the policy's owner - * so we can restrict who modifies it (because of the "s3:CreateBucket" action). - */ - @Override - public BucketPolicyVO getByName( String bucketName ) { - SearchBuilder searchByBucket = createSearchBuilder(); - searchByBucket.and("BucketName", searchByBucket.entity().getBucketName(), SearchCriteria.Op.EQ); - Transaction txn = Transaction.open(Transaction.AWSAPI_DB); + /** + * Since a bucket policy can exist before its bucket we also need to keep the policy's owner + * so we can restrict who modifies it (because of the "s3:CreateBucket" action). + */ + @Override + public BucketPolicyVO getByName( String bucketName ) { + SearchBuilder searchByBucket = createSearchBuilder(); + searchByBucket.and("BucketName", searchByBucket.entity().getBucketName(), SearchCriteria.Op.EQ); + Transaction txn = Transaction.open(Transaction.AWSAPI_DB); try { txn.start(); SearchCriteria sc = searchByBucket.create(); sc.setParameters("BucketName", bucketName); return findOneBy(sc); - }finally { - txn.close(); - } - - } - - @Override - public void deletePolicy( String bucketName ) { - SearchBuilder deleteByBucket = createSearchBuilder(); - deleteByBucket.and("BucketName", deleteByBucket.entity().getBucketName(), SearchCriteria.Op.EQ); - Transaction txn = Transaction.open(Transaction.AWSAPI_DB); - try { + }finally { + txn.close(); + } + + } + + @Override + public void deletePolicy( String bucketName ) { + SearchBuilder deleteByBucket = createSearchBuilder(); + deleteByBucket.and("BucketName", deleteByBucket.entity().getBucketName(), SearchCriteria.Op.EQ); + Transaction txn = Transaction.open(Transaction.AWSAPI_DB); + try { txn.start(); SearchCriteria sc = deleteByBucket.create(); sc.setParameters("BucketName", bucketName); remove(sc); - - }finally { - txn.close(); - } - - } + + }finally { + txn.close(); + } + + } } diff --git a/awsapi/src/com/cloud/bridge/persist/dao/CloudStackConfigurationDaoImpl.java b/awsapi/src/com/cloud/bridge/persist/dao/CloudStackConfigurationDaoImpl.java index 511cfa73946..e77061169d9 100644 --- a/awsapi/src/com/cloud/bridge/persist/dao/CloudStackConfigurationDaoImpl.java +++ b/awsapi/src/com/cloud/bridge/persist/dao/CloudStackConfigurationDaoImpl.java @@ -16,18 +16,12 @@ // under the License. package com.cloud.bridge.persist.dao; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; - import javax.ejb.Local; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.bridge.model.CloudStackConfigurationVO; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; @@ -37,31 +31,31 @@ import com.cloud.utils.db.Transaction; @Component @Local(value={CloudStackConfigurationDao.class}) public class CloudStackConfigurationDaoImpl extends GenericDaoBase implements CloudStackConfigurationDao { - private static final Logger s_logger = Logger.getLogger(CloudStackConfigurationDaoImpl.class); - - final SearchBuilder NameSearch= createSearchBuilder(); - - public CloudStackConfigurationDaoImpl() { } - - - @Override - @DB - public String getConfigValue(String name) { + private static final Logger s_logger = Logger.getLogger(CloudStackConfigurationDaoImpl.class); + + final SearchBuilder NameSearch= createSearchBuilder(); + + public CloudStackConfigurationDaoImpl() { } + + + @Override + @DB + public String getConfigValue(String name) { NameSearch.and("name", NameSearch.entity().getName(), SearchCriteria.Op.EQ); Transaction txn = Transaction.currentTxn(); - try { - txn.start(); - SearchCriteria sc = NameSearch.create(); - sc.setParameters("name", name); - CloudStackConfigurationVO configItem = findOneBy(sc); - if (configItem == null) { - s_logger.warn("No configuration item found with name " + name); - return null; - } - return configItem.getValue(); + try { + txn.start(); + SearchCriteria sc = NameSearch.create(); + sc.setParameters("name", name); + CloudStackConfigurationVO configItem = findOneBy(sc); + if (configItem == null) { + s_logger.warn("No configuration item found with name " + name); + return null; + } + return configItem.getValue(); }finally { - - } - } - + + } + } + } diff --git a/awsapi/src/com/cloud/bridge/persist/dao/MultipartLoadDao.java b/awsapi/src/com/cloud/bridge/persist/dao/MultipartLoadDao.java index c9b5ec75b5f..c1a69dc5e47 100644 --- a/awsapi/src/com/cloud/bridge/persist/dao/MultipartLoadDao.java +++ b/awsapi/src/com/cloud/bridge/persist/dao/MultipartLoadDao.java @@ -16,21 +16,13 @@ // under the License. package com.cloud.bridge.persist.dao; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Timestamp; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; -import java.util.Properties; + +import javax.inject.Inject; import org.apache.log4j.Logger; @@ -40,71 +32,69 @@ import com.cloud.bridge.model.MultipartMetaVO; import com.cloud.bridge.service.core.s3.S3MetaDataEntry; import com.cloud.bridge.service.core.s3.S3MultipartPart; import com.cloud.bridge.service.core.s3.S3MultipartUpload; -import com.cloud.bridge.util.ConfigurationHelper; import com.cloud.bridge.util.OrderedPair; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.Transaction; public class MultipartLoadDao { - public static final Logger logger = Logger.getLogger(MultipartLoadDao.class); - - protected final MultipartMetaDao mpartMetaDao = ComponentLocator.inject(MultipartMetaDaoImpl.class); - protected final MultiPartPartsDao mpartPartsDao = ComponentLocator.inject(MultiPartPartsDaoImpl.class); - protected final MultiPartUploadsDao mpartUploadDao = ComponentLocator.inject(MultiPartUploadsDaoImpl.class); - - public MultipartLoadDao() {} - - /** - * If a multipart upload exists with the uploadId value then return the non-null creators - * accessKey. - * - * @param uploadId - * @return creator of the multipart upload, and NameKey of upload - */ - - - public OrderedPair multipartExits( int uploadId ) - throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException - { - return mpartUploadDao.multipartExits(uploadId); - } - - /** - * The multipart upload was either successfully completed or was aborted. In either case, we need - * to remove all of its state from the tables. Note that we have cascade deletes so all tables with - * uploadId as a foreign key are automatically cleaned. - * - * @param uploadId - * - */ - public void deleteUpload( int uploadId ) { - mpartUploadDao.deleteUpload(uploadId); - } - - /** - * The caller needs to know who initiated the multipart upload. - * - * @param uploadId - * @return the access key value defining the initiator - */ - public String getInitiator( int uploadId ) { - return mpartUploadDao.getAtrributeValue("AccessKey", uploadId); - } - - /** - * Create a new "in-process" multipart upload entry to keep track of its state. - * - * @param accessKey - * @param bucketName - * @param key - * @param cannedAccess - * - * @return if positive its the uploadId to be returned to the client - * - */ - public int initiateUpload( String accessKey, String bucketName, String key, String cannedAccess, S3MetaDataEntry[] meta ) { - int uploadId = -1; - Transaction txn = null; + public static final Logger logger = Logger.getLogger(MultipartLoadDao.class); + + @Inject MultipartMetaDao mpartMetaDao; + @Inject MultiPartPartsDao mpartPartsDao; + @Inject MultiPartUploadsDao mpartUploadDao; + + public MultipartLoadDao() {} + + /** + * If a multipart upload exists with the uploadId value then return the non-null creators + * accessKey. + * + * @param uploadId + * @return creator of the multipart upload, and NameKey of upload + */ + + + public OrderedPair multipartExits( int uploadId ) + throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException + { + return mpartUploadDao.multipartExits(uploadId); + } + + /** + * The multipart upload was either successfully completed or was aborted. In either case, we need + * to remove all of its state from the tables. Note that we have cascade deletes so all tables with + * uploadId as a foreign key are automatically cleaned. + * + * @param uploadId + * + */ + public void deleteUpload( int uploadId ) { + mpartUploadDao.deleteUpload(uploadId); + } + + /** + * The caller needs to know who initiated the multipart upload. + * + * @param uploadId + * @return the access key value defining the initiator + */ + public String getInitiator( int uploadId ) { + return mpartUploadDao.getAtrributeValue("AccessKey", uploadId); + } + + /** + * Create a new "in-process" multipart upload entry to keep track of its state. + * + * @param accessKey + * @param bucketName + * @param key + * @param cannedAccess + * + * @return if positive its the uploadId to be returned to the client + * + */ + public int initiateUpload( String accessKey, String bucketName, String key, String cannedAccess, S3MetaDataEntry[] meta ) { + int uploadId = -1; + Transaction txn = null; try { txn = Transaction.open(Transaction.AWSAPI_DB); Date tod = new Date(); @@ -126,26 +116,26 @@ public class MultipartLoadDao { txn.commit(); } } - + return uploadId; } finally { txn.close(); } } - - /** - * Remember all the individual parts that make up the entire multipart upload so that once - * the upload is complete all the parts can be glued together into a single object. Note, - * the caller can over write an existing part. - * - * @param uploadId - * @param partNumber - * @param md5 - * @param storedPath - * @param size - * @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException - */ - public void savePart( int uploadId, int partNumber, String md5, String storedPath, int size ) { + + /** + * Remember all the individual parts that make up the entire multipart upload so that once + * the upload is complete all the parts can be glued together into a single object. Note, + * the caller can over write an existing part. + * + * @param uploadId + * @param partNumber + * @param md5 + * @param storedPath + * @param size + * @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException + */ + public void savePart( int uploadId, int partNumber, String md5, String storedPath, int size ) { try { MultiPartPartsVO partVO = null; @@ -169,32 +159,32 @@ public class MultipartLoadDao { } finally { } } - - /** - * It is possible for there to be a null canned access policy defined. - * @param uploadId - * @return the value defined in the x-amz-acl header or null - */ - public String getCannedAccess( int uploadId ) { - return mpartUploadDao.getAtrributeValue("x_amz_acl", uploadId); - } - - /** - * When the multipart are being composed into one object we need any meta data to be saved with - * the new re-constituted object. - * - * @param uploadId - * @return an array of S3MetaDataEntry (will be null if no meta values exist) - * @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException - */ - public S3MetaDataEntry[] getMeta( int uploadId ) - throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException - { - List metaList = new ArrayList(); - int count = 0; - List metaVO; + + /** + * It is possible for there to be a null canned access policy defined. + * @param uploadId + * @return the value defined in the x-amz-acl header or null + */ + public String getCannedAccess( int uploadId ) { + return mpartUploadDao.getAtrributeValue("x_amz_acl", uploadId); + } + + /** + * When the multipart are being composed into one object we need any meta data to be saved with + * the new re-constituted object. + * + * @param uploadId + * @return an array of S3MetaDataEntry (will be null if no meta values exist) + * @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException + */ + public S3MetaDataEntry[] getMeta( int uploadId ) + throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException + { + List metaList = new ArrayList(); + int count = 0; + List metaVO; try { - + metaVO = mpartMetaDao.getByUploadID(uploadId); for (MultipartMetaVO multipartMetaVO : metaVO) { S3MetaDataEntry oneMeta = new S3MetaDataEntry(); @@ -203,42 +193,42 @@ public class MultipartLoadDao { metaList.add( oneMeta ); count++; } - + if ( 0 == count ) return null; else return metaList.toArray(new S3MetaDataEntry[0]); - + } finally { } - } - - /** - * The result has to be ordered by key and if there is more than one identical key then all the - * identical keys are ordered by create time. - * - * @param bucketName - * @param maxParts - * @param prefix - can be null - * @param keyMarker - can be null - * @param uploadIdMarker - can be null, should only be defined if keyMarker is not-null - * @return OrderedPair - * @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException - */ - public OrderedPair getInitiatedUploads( String bucketName, int maxParts, String prefix, String keyMarker, String uploadIdMarker ) - throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException - { - S3MultipartUpload[] inProgress = new S3MultipartUpload[maxParts]; - boolean isTruncated = false; - int i = 0; - int pos = 1; - List uploadList; - // -> SQL like condition requires the '%' as a wildcard marker - if (null != prefix) prefix = prefix + "%"; - + } + + /** + * The result has to be ordered by key and if there is more than one identical key then all the + * identical keys are ordered by create time. + * + * @param bucketName + * @param maxParts + * @param prefix - can be null + * @param keyMarker - can be null + * @param uploadIdMarker - can be null, should only be defined if keyMarker is not-null + * @return OrderedPair + * @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException + */ + public OrderedPair getInitiatedUploads( String bucketName, int maxParts, String prefix, String keyMarker, String uploadIdMarker ) + throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException + { + S3MultipartUpload[] inProgress = new S3MultipartUpload[maxParts]; + boolean isTruncated = false; + int i = 0; + int pos = 1; + List uploadList; + // -> SQL like condition requires the '%' as a wildcard marker + if (null != prefix) prefix = prefix + "%"; + try { - uploadList = mpartUploadDao.getInitiatedUploads(bucketName, maxParts, prefix, keyMarker, uploadIdMarker); + uploadList = mpartUploadDao.getInitiatedUploads(bucketName, maxParts, prefix, keyMarker, uploadIdMarker); for (MultiPartUploadsVO uploadsVO : uploadList) { Calendar tod = Calendar.getInstance(); tod.setTime(uploadsVO.getCreateTime()); @@ -258,33 +248,33 @@ public class MultipartLoadDao { }finally { } - } - - /** - * Return info on a range of upload parts that have already been stored in disk. - * Note that parts can be uploaded in any order yet we must returned an ordered list - * of parts thus we use the "ORDERED BY" clause to sort the list. - * - * @param uploadId - * @param maxParts - * @param startAt - * @return an array of S3MultipartPart objects - * @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException - */ - public S3MultipartPart[] getParts( int uploadId, int maxParts, int startAt ) - throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException - { - S3MultipartPart[] parts = new S3MultipartPart[maxParts]; - int i = 0; - List partsVO; - try { - - partsVO = mpartPartsDao.getParts(uploadId, startAt + maxParts + 1, startAt); - - for (MultiPartPartsVO partVO : partsVO) { + } + + /** + * Return info on a range of upload parts that have already been stored in disk. + * Note that parts can be uploaded in any order yet we must returned an ordered list + * of parts thus we use the "ORDERED BY" clause to sort the list. + * + * @param uploadId + * @param maxParts + * @param startAt + * @return an array of S3MultipartPart objects + * @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException + */ + public S3MultipartPart[] getParts( int uploadId, int maxParts, int startAt ) + throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException + { + S3MultipartPart[] parts = new S3MultipartPart[maxParts]; + int i = 0; + List partsVO; + try { + + partsVO = mpartPartsDao.getParts(uploadId, startAt + maxParts + 1, startAt); + + for (MultiPartPartsVO partVO : partsVO) { Calendar tod = Calendar.getInstance(); tod.setTime(partVO.getCreateTime()); - + parts[i] = new S3MultipartPart(); parts[i].setPartNumber(partVO.getPartNumber()); parts[i].setEtag(partVO.getMd5()); @@ -293,74 +283,74 @@ public class MultipartLoadDao { parts[i].setPath(partVO.getStoredPath()); i++; } - + if (i < maxParts) parts = (S3MultipartPart[])resizeArray(parts,i); return parts; - + } finally { } - } - - /** - * How many parts exist after the endMarker part number? - * - * @param uploadId - * @param endMarker - can be used to see if getUploadedParts was truncated - * @return number of parts with partNumber greater than endMarker - * @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException - */ - public int numParts( int uploadId, int endMarker ) { - return mpartPartsDao.getnumParts(uploadId, endMarker); + } + + /** + * How many parts exist after the endMarker part number? + * + * @param uploadId + * @param endMarker - can be used to see if getUploadedParts was truncated + * @return number of parts with partNumber greater than endMarker + * @throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException + */ + public int numParts( int uploadId, int endMarker ) { + return mpartPartsDao.getnumParts(uploadId, endMarker); } - /** - * A multipart upload request can have zero to many meta data entries to be applied to the - * final object. We need to remember all of the objects meta data until the multipart is complete. - * - * @param uploadId - defines an in-process multipart upload - * @param meta - an array of meta data to be assocated with the uploadId value - * - */ - private void saveMultipartMeta( int uploadId, S3MetaDataEntry[] meta ) { - if (null == meta) return; - - Transaction txn = null; + /** + * A multipart upload request can have zero to many meta data entries to be applied to the + * final object. We need to remember all of the objects meta data until the multipart is complete. + * + * @param uploadId - defines an in-process multipart upload + * @param meta - an array of meta data to be assocated with the uploadId value + * + */ + private void saveMultipartMeta( int uploadId, S3MetaDataEntry[] meta ) { + if (null == meta) return; + + Transaction txn = null; try { txn = Transaction.open(Transaction.AWSAPI_DB); for( int i=0; i < meta.length; i++ ) { - S3MetaDataEntry entry = meta[i]; - MultipartMetaVO metaVO = new MultipartMetaVO(); - metaVO.setUploadID(uploadId); - metaVO.setName(entry.getName()); - metaVO.setValue(entry.getValue()); - metaVO=mpartMetaDao.persist(metaVO); + S3MetaDataEntry entry = meta[i]; + MultipartMetaVO metaVO = new MultipartMetaVO(); + metaVO.setUploadID(uploadId); + metaVO.setName(entry.getName()); + metaVO.setValue(entry.getValue()); + metaVO=mpartMetaDao.persist(metaVO); } txn.commit(); } finally { txn.close(); } - } - + } - /** - * Reallocates an array with a new size, and copies the contents - * of the old array to the new array. - * - * @param oldArray the old array, to be reallocated. - * @param newSize the new array size. - * @return A new array with the same contents. - */ + + /** + * Reallocates an array with a new size, and copies the contents + * of the old array to the new array. + * + * @param oldArray the old array, to be reallocated. + * @param newSize the new array size. + * @return A new array with the same contents. + */ private static Object resizeArray(Object oldArray, int newSize) { - int oldSize = java.lang.reflect.Array.getLength(oldArray); - Class elementType = oldArray.getClass().getComponentType(); - Object newArray = java.lang.reflect.Array.newInstance( - elementType,newSize); - int preserveLength = Math.min(oldSize,newSize); - if (preserveLength > 0) - System.arraycopy (oldArray,0,newArray,0,preserveLength); - return newArray; + int oldSize = java.lang.reflect.Array.getLength(oldArray); + Class elementType = oldArray.getClass().getComponentType(); + Object newArray = java.lang.reflect.Array.newInstance( + elementType,newSize); + int preserveLength = Math.min(oldSize,newSize); + if (preserveLength > 0) + System.arraycopy (oldArray,0,newArray,0,preserveLength); + return newArray; } } diff --git a/awsapi/src/com/cloud/bridge/persist/dao/SObjectDaoImpl.java b/awsapi/src/com/cloud/bridge/persist/dao/SObjectDaoImpl.java index 3e6815279bd..6d23757b8b5 100644 --- a/awsapi/src/com/cloud/bridge/persist/dao/SObjectDaoImpl.java +++ b/awsapi/src/com/cloud/bridge/persist/dao/SObjectDaoImpl.java @@ -22,16 +22,13 @@ import java.util.List; import java.util.Set; import javax.ejb.Local; +import javax.inject.Inject; import org.springframework.stereotype.Component; -import com.cloud.bridge.model.SBucket; import com.cloud.bridge.model.SBucketVO; import com.cloud.bridge.model.SObjectItemVO; import com.cloud.bridge.model.SObjectVO; -import com.cloud.bridge.util.EntityParam; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -40,18 +37,18 @@ import com.cloud.utils.db.Transaction; @Component @Local(value={SObjectDao.class}) public class SObjectDaoImpl extends GenericDaoBase implements SObjectDao { - protected final SObjectItemDao itemDao = ComponentLocator.inject(SObjectItemDaoImpl.class); - - public SObjectDaoImpl() {} + @Inject SObjectItemDao itemDao; - @Override - public SObjectVO getByNameKey(SBucketVO bucket, String nameKey) { - SObjectVO object = null; - SearchBuilder SearchByName = createSearchBuilder(); - SearchByName.and("SBucketID", SearchByName.entity().getBucketID() , SearchCriteria.Op.EQ); - SearchByName.and("NameKey", SearchByName.entity().getNameKey() , SearchCriteria.Op.EQ); - Transaction txn = Transaction.open(Transaction.AWSAPI_DB); - try { + public SObjectDaoImpl() {} + + @Override + public SObjectVO getByNameKey(SBucketVO bucket, String nameKey) { + SObjectVO object = null; + SearchBuilder SearchByName = createSearchBuilder(); + SearchByName.and("SBucketID", SearchByName.entity().getBucketID() , SearchCriteria.Op.EQ); + SearchByName.and("NameKey", SearchByName.entity().getNameKey() , SearchCriteria.Op.EQ); + Transaction txn = Transaction.open(Transaction.AWSAPI_DB); + try { txn.start(); SearchCriteria sc = SearchByName.create(); sc.setParameters("SBucketID", bucket.getId()); @@ -62,23 +59,23 @@ public class SObjectDaoImpl extends GenericDaoBase implements S itemDao.getItems(object.getId())); object.setItems(items); } - return object; - - }finally { + return object; + + }finally { txn.close(); - } - - } - - @Override - public List listBucketObjects(SBucketVO bucket, String prefix, String marker, int maxKeys) { - StringBuffer sb = new StringBuffer(); - List params = new ArrayList(); - SearchBuilder SearchByBucket = createSearchBuilder(); - List objects = new ArrayList(); - - SearchByBucket.and("SBucketID", SearchByBucket.entity().getBucketID(), SearchCriteria.Op.EQ); - SearchByBucket.and("DeletionMark", SearchByBucket.entity().getDeletionMark(), SearchCriteria.Op.NULL); + } + + } + + @Override + public List listBucketObjects(SBucketVO bucket, String prefix, String marker, int maxKeys) { + StringBuffer sb = new StringBuffer(); + List params = new ArrayList(); + SearchBuilder SearchByBucket = createSearchBuilder(); + List objects = new ArrayList(); + + SearchByBucket.and("SBucketID", SearchByBucket.entity().getBucketID(), SearchCriteria.Op.EQ); + SearchByBucket.and("DeletionMark", SearchByBucket.entity().getDeletionMark(), SearchCriteria.Op.NULL); Transaction txn = Transaction.currentTxn(); // Transaction.open("cloudbridge", Transaction.AWSAPI_DB, true); try { txn.start(); @@ -91,19 +88,19 @@ public class SObjectDaoImpl extends GenericDaoBase implements S } return objects; }finally { - txn.close(); + txn.close(); } - } - - @Override - public List listAllBucketObjects(SBucketVO bucket, String prefix, String marker, int maxKeys) { - StringBuffer sb = new StringBuffer(); - List params = new ArrayList(); - SearchBuilder getAllBuckets = createSearchBuilder(); - List objects = new ArrayList(); - getAllBuckets.and("SBucketID", getAllBuckets.entity().getBucketID(), SearchCriteria.Op.EQ); + } - Transaction txn = Transaction.currentTxn(); // Transaction.open("cloudbridge", Transaction.AWSAPI_DB, true); + @Override + public List listAllBucketObjects(SBucketVO bucket, String prefix, String marker, int maxKeys) { + StringBuffer sb = new StringBuffer(); + List params = new ArrayList(); + SearchBuilder getAllBuckets = createSearchBuilder(); + List objects = new ArrayList(); + getAllBuckets.and("SBucketID", getAllBuckets.entity().getBucketID(), SearchCriteria.Op.EQ); + + Transaction txn = Transaction.currentTxn(); // Transaction.open("cloudbridge", Transaction.AWSAPI_DB, true); try { txn.start(); SearchCriteria sc = getAllBuckets.create(); @@ -115,8 +112,8 @@ public class SObjectDaoImpl extends GenericDaoBase implements S } return objects; }finally { - txn.close(); + txn.close(); } - - } + + } } diff --git a/awsapi/src/com/cloud/bridge/service/EC2MainServlet.java b/awsapi/src/com/cloud/bridge/service/EC2MainServlet.java index e8ccb0c6e8d..f5a2d21e134 100644 --- a/awsapi/src/com/cloud/bridge/service/EC2MainServlet.java +++ b/awsapi/src/com/cloud/bridge/service/EC2MainServlet.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.util.UUID; +import javax.inject.Inject; import javax.servlet.RequestDispatcher; import javax.servlet.ServletConfig; import javax.servlet.ServletException; @@ -30,50 +31,48 @@ import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import com.cloud.bridge.persist.dao.CloudStackConfigurationDao; -import com.cloud.bridge.persist.dao.CloudStackConfigurationDaoImpl; import com.cloud.bridge.util.ConfigurationHelper; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; -import com.cloud.utils.db.Transaction; - -import net.sf.ehcache.Cache; @DB public class EC2MainServlet extends HttpServlet{ - private static final long serialVersionUID = 2201599478145974479L; - - public static final String EC2_REST_SERVLET_PATH="/rest/AmazonEC2/"; - public static final String EC2_SOAP_SERVLET_PATH="/services/AmazonEC2/"; - public static final String ENABLE_EC2_API="enable.ec2.api"; - private static boolean isEC2APIEnabled = false; - public static final Logger logger = Logger.getLogger(EC2MainServlet.class); - CloudStackConfigurationDao csDao = ComponentLocator.inject(CloudStackConfigurationDaoImpl.class); - - /** - * We build the path to where the keystore holding the WS-Security X509 certificates - * are stored. - */ - @DB - public void init( ServletConfig config ) throws ServletException { - try{ - ConfigurationHelper.preConfigureConfigPathFromServletContext(config.getServletContext()); - // check if API is enabled - String value = csDao.getConfigValue(ENABLE_EC2_API); - if(value != null){ - isEC2APIEnabled = Boolean.valueOf(value); - } - logger.info("Value of EC2 API Flag ::" + value); - }catch(Exception e){ - throw new ServletException("Error initializing awsapi: " + e.getMessage(), e); - } - } - - protected void doGet(HttpServletRequest req, HttpServletResponse resp) { - doGetOrPost(req, resp); + private static final long serialVersionUID = 2201599478145974479L; + + public static final String EC2_REST_SERVLET_PATH="/rest/AmazonEC2/"; + public static final String EC2_SOAP_SERVLET_PATH="/services/AmazonEC2/"; + public static final String ENABLE_EC2_API="enable.ec2.api"; + private static boolean isEC2APIEnabled = false; + public static final Logger logger = Logger.getLogger(EC2MainServlet.class); + @Inject CloudStackConfigurationDao csDao; + + /** + * We build the path to where the keystore holding the WS-Security X509 certificates + * are stored. + */ + @Override + @DB + public void init( ServletConfig config ) throws ServletException { + try{ + ConfigurationHelper.preConfigureConfigPathFromServletContext(config.getServletContext()); + // check if API is enabled + String value = csDao.getConfigValue(ENABLE_EC2_API); + if(value != null){ + isEC2APIEnabled = Boolean.valueOf(value); + } + logger.info("Value of EC2 API Flag ::" + value); + }catch(Exception e){ + throw new ServletException("Error initializing awsapi: " + e.getMessage(), e); + } } - + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + doGetOrPost(req, resp); + } + + @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) { - doGetOrPost(req, resp); + doGetOrPost(req, resp); } protected void doGetOrPost(HttpServletRequest request, HttpServletResponse response) { @@ -84,30 +83,30 @@ public class EC2MainServlet extends HttpServlet{ faultResponse(response, "404" , "EC2 API is disabled."); return; } - - if(action != null){ - //We presume it's a Query/Rest call - try { - RequestDispatcher dispatcher = request.getRequestDispatcher(EC2_REST_SERVLET_PATH); - dispatcher.forward(request, response); - } catch (ServletException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - else { - try { - request.getRequestDispatcher(EC2_SOAP_SERVLET_PATH).forward(request, response); - } catch (ServletException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - + + if(action != null){ + //We presume it's a Query/Rest call + try { + RequestDispatcher dispatcher = request.getRequestDispatcher(EC2_REST_SERVLET_PATH); + dispatcher.forward(request, response); + } catch (ServletException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + else { + try { + request.getRequestDispatcher(EC2_SOAP_SERVLET_PATH).forward(request, response); + } catch (ServletException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } - + private void faultResponse(HttpServletResponse response, String errorCode, String errorMessage) { try { OutputStreamWriter out = new OutputStreamWriter(response.getOutputStream()); diff --git a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java index 4f748731504..8309bfd73f4 100644 --- a/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java +++ b/awsapi/src/com/cloud/bridge/service/EC2RestServlet.java @@ -39,6 +39,7 @@ import java.util.List; import java.util.Properties; import java.util.UUID; +import javax.inject.Inject; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -132,184 +133,183 @@ import com.cloud.bridge.service.core.ec2.EC2StopInstances; import com.cloud.bridge.service.core.ec2.EC2Volume; import com.cloud.bridge.service.core.ec2.EC2VolumeFilterSet; import com.cloud.bridge.service.exception.EC2ServiceException; +import com.cloud.bridge.service.exception.EC2ServiceException.ClientError; import com.cloud.bridge.service.exception.NoSuchObjectException; import com.cloud.bridge.service.exception.PermissionDeniedException; -import com.cloud.bridge.service.exception.EC2ServiceException.ClientError; import com.cloud.bridge.util.AuthenticationUtils; import com.cloud.bridge.util.ConfigurationHelper; import com.cloud.bridge.util.EC2RestAuth; import com.cloud.stack.models.CloudStackAccount; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.Transaction; public class EC2RestServlet extends HttpServlet { - private static final long serialVersionUID = -6168996266762804888L; - protected final UserCredentialsDaoImpl ucDao = ComponentLocator.inject(UserCredentialsDaoImpl.class); - protected final OfferingDaoImpl ofDao = ComponentLocator.inject(OfferingDaoImpl.class); - - public static final Logger logger = Logger.getLogger(EC2RestServlet.class); - - private OMFactory factory = OMAbstractFactory.getOMFactory(); - private XMLOutputFactory xmlOutFactory = XMLOutputFactory.newInstance(); - - private String pathToKeystore = null; - private String keystorePassword = null; - private String wsdlVersion = null; - private String version = null; - - boolean debug=true; + private static final long serialVersionUID = -6168996266762804888L; + @Inject UserCredentialsDaoImpl ucDao; + @Inject OfferingDaoImpl ofDao; - - /** - * We build the path to where the keystore holding the WS-Security X509 certificates - * are stored. - */ - @Override - public void init( ServletConfig config ) throws ServletException { - File propertiesFile = ConfigurationHelper.findConfigurationFile("ec2-service.properties"); - Properties EC2Prop = null; - - if (null != propertiesFile) { - logger.info("Use EC2 properties file: " + propertiesFile.getAbsolutePath()); - EC2Prop = new Properties(); - try { - EC2Prop.load( new FileInputStream( propertiesFile )); - } catch (FileNotFoundException e) { - logger.warn("Unable to open properties file: " + propertiesFile.getAbsolutePath(), e); - } catch (IOException e) { - logger.warn("Unable to read properties file: " + propertiesFile.getAbsolutePath(), e); - } - String keystore = EC2Prop.getProperty( "keystore" ); - keystorePassword = EC2Prop.getProperty( "keystorePass" ); - wsdlVersion = EC2Prop.getProperty( "WSDLVersion", "2010-11-15" ); - version = EC2Prop.getProperty( "cloudbridgeVersion", "UNKNOWN VERSION" ); - - String installedPath = System.getenv("CATALINA_HOME"); - if (installedPath == null) installedPath = System.getenv("CATALINA_BASE"); - if (installedPath == null) installedPath = System.getProperty("catalina.home"); - String webappPath = config.getServletContext().getRealPath("/"); - //pathToKeystore = new String( installedPath + File.separator + "webapps" + File.separator + webappName + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + keystore ); - pathToKeystore = new String( webappPath + "WEB-INF" + File.separator + "classes" + File.separator + keystore ); - } - } - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) { - doGetOrPost(req, resp); - } - + public static final Logger logger = Logger.getLogger(EC2RestServlet.class); + + private final OMFactory factory = OMAbstractFactory.getOMFactory(); + private final XMLOutputFactory xmlOutFactory = XMLOutputFactory.newInstance(); + + private String pathToKeystore = null; + private String keystorePassword = null; + private String wsdlVersion = null; + private String version = null; + + boolean debug=true; + + + /** + * We build the path to where the keystore holding the WS-Security X509 certificates + * are stored. + */ @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) { - doGetOrPost(req, resp); + public void init( ServletConfig config ) throws ServletException { + File propertiesFile = ConfigurationHelper.findConfigurationFile("ec2-service.properties"); + Properties EC2Prop = null; + + if (null != propertiesFile) { + logger.info("Use EC2 properties file: " + propertiesFile.getAbsolutePath()); + EC2Prop = new Properties(); + try { + EC2Prop.load( new FileInputStream( propertiesFile )); + } catch (FileNotFoundException e) { + logger.warn("Unable to open properties file: " + propertiesFile.getAbsolutePath(), e); + } catch (IOException e) { + logger.warn("Unable to read properties file: " + propertiesFile.getAbsolutePath(), e); + } + String keystore = EC2Prop.getProperty( "keystore" ); + keystorePassword = EC2Prop.getProperty( "keystorePass" ); + wsdlVersion = EC2Prop.getProperty( "WSDLVersion", "2010-11-15" ); + version = EC2Prop.getProperty( "cloudbridgeVersion", "UNKNOWN VERSION" ); + + String installedPath = System.getenv("CATALINA_HOME"); + if (installedPath == null) installedPath = System.getenv("CATALINA_BASE"); + if (installedPath == null) installedPath = System.getProperty("catalina.home"); + String webappPath = config.getServletContext().getRealPath("/"); + //pathToKeystore = new String( installedPath + File.separator + "webapps" + File.separator + webappName + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + keystore ); + pathToKeystore = new String( webappPath + "WEB-INF" + File.separator + "classes" + File.separator + keystore ); + } + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + doGetOrPost(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) { + doGetOrPost(req, resp); } protected void doGetOrPost(HttpServletRequest request, HttpServletResponse response) { - - if(debug){ - System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.request_uri: "+request.getAttribute("javax.servlet.forward.request_uri")); - System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.context_path: "+request.getAttribute("javax.servlet.forward.context_path")); - System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.servlet_path: "+request.getAttribute("javax.servlet.forward.servlet_path")); - System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.path_info: "+request.getAttribute("javax.servlet.forward.path_info")); - System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.query_string: "+request.getAttribute("javax.servlet.forward.query_string")); - - } - - - String action = request.getParameter( "Action" ); - logRequest(request); - - // -> unauthenticated calls, should still be done over HTTPS - if (action.equalsIgnoreCase( "SetUserKeys" )) { - setUserKeys(request, response); - return; - } - if (action.equalsIgnoreCase( "CloudEC2Version" )) { - cloudEC2Version(request, response); - return; - } + if(debug){ + System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.request_uri: "+request.getAttribute("javax.servlet.forward.request_uri")); + System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.context_path: "+request.getAttribute("javax.servlet.forward.context_path")); + System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.servlet_path: "+request.getAttribute("javax.servlet.forward.servlet_path")); + System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.path_info: "+request.getAttribute("javax.servlet.forward.path_info")); + System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.query_string: "+request.getAttribute("javax.servlet.forward.query_string")); - // -> authenticated calls + } + + + String action = request.getParameter( "Action" ); + logRequest(request); + + // -> unauthenticated calls, should still be done over HTTPS + if (action.equalsIgnoreCase( "SetUserKeys" )) { + setUserKeys(request, response); + return; + } + + if (action.equalsIgnoreCase( "CloudEC2Version" )) { + cloudEC2Version(request, response); + return; + } + + // -> authenticated calls try { - if (!authenticateRequest( request, response )) return; + if (!authenticateRequest( request, response )) return; + + if (action.equalsIgnoreCase( "AllocateAddress" )) allocateAddress(request, response); + else if (action.equalsIgnoreCase( "AssociateAddress" )) associateAddress(request, response); + else if (action.equalsIgnoreCase( "AttachVolume" )) attachVolume(request, response ); + else if (action.equalsIgnoreCase( "AuthorizeSecurityGroupIngress" )) authorizeSecurityGroupIngress(request, response); + else if (action.equalsIgnoreCase( "CreateImage" )) createImage(request, response); + else if (action.equalsIgnoreCase( "CreateSecurityGroup" )) createSecurityGroup(request, response); + else if (action.equalsIgnoreCase( "CreateSnapshot" )) createSnapshot(request, response); + else if (action.equalsIgnoreCase( "CreateVolume" )) createVolume(request, response); + else if (action.equalsIgnoreCase( "DeleteSecurityGroup" )) deleteSecurityGroup(request, response); + else if (action.equalsIgnoreCase( "DeleteSnapshot" )) deleteSnapshot(request, response); + else if (action.equalsIgnoreCase( "DeleteVolume" )) deleteVolume(request, response); + else if (action.equalsIgnoreCase( "DeregisterImage" )) deregisterImage(request, response); + else if (action.equalsIgnoreCase( "DescribeAddresses" )) describeAddresses(request, response); + else if (action.equalsIgnoreCase( "DescribeAvailabilityZones" )) describeAvailabilityZones(request, response); + else if (action.equalsIgnoreCase( "DescribeImageAttribute" )) describeImageAttribute(request, response); + else if (action.equalsIgnoreCase( "DescribeImages" )) describeImages(request, response); + else if (action.equalsIgnoreCase( "DescribeInstanceAttribute" )) describeInstanceAttribute(request, response); + else if (action.equalsIgnoreCase( "DescribeInstances" )) describeInstances(request, response); + else if (action.equalsIgnoreCase( "DescribeSecurityGroups" )) describeSecurityGroups(request, response); + else if (action.equalsIgnoreCase( "DescribeSnapshots" )) describeSnapshots(request, response); + else if (action.equalsIgnoreCase( "DescribeVolumes" )) describeVolumes(request, response); + else if (action.equalsIgnoreCase( "DetachVolume" )) detachVolume(request, response); + else if (action.equalsIgnoreCase( "DisassociateAddress" )) disassociateAddress(request, response); + else if (action.equalsIgnoreCase( "ModifyImageAttribute" )) modifyImageAttribute(request, response); + else if (action.equalsIgnoreCase( "RebootInstances" )) rebootInstances(request, response); + else if (action.equalsIgnoreCase( "RegisterImage" )) registerImage(request, response); + else if (action.equalsIgnoreCase( "ReleaseAddress" )) releaseAddress(request, response); + else if (action.equalsIgnoreCase( "ResetImageAttribute" )) resetImageAttribute(request, response); + else if (action.equalsIgnoreCase( "RevokeSecurityGroupIngress")) revokeSecurityGroupIngress(request, response); + else if (action.equalsIgnoreCase( "RunInstances" )) runInstances(request, response); + else if (action.equalsIgnoreCase( "StartInstances" )) startInstances(request, response); + else if (action.equalsIgnoreCase( "StopInstances" )) stopInstances(request, response); + else if (action.equalsIgnoreCase( "TerminateInstances" )) terminateInstances(request, response); + else if (action.equalsIgnoreCase( "SetCertificate" )) setCertificate(request, response); + else if (action.equalsIgnoreCase( "DeleteCertificate" )) deleteCertificate(request, response); + else if (action.equalsIgnoreCase( "SetOfferMapping" )) setOfferMapping(request, response); + else if (action.equalsIgnoreCase( "DeleteOfferMapping" )) deleteOfferMapping(request, response); + else if (action.equalsIgnoreCase( "CreateKeyPair" )) createKeyPair(request, response); + else if (action.equalsIgnoreCase( "ImportKeyPair" )) importKeyPair(request, response); + else if (action.equalsIgnoreCase( "DeleteKeyPair" )) deleteKeyPair(request, response); + else if (action.equalsIgnoreCase( "DescribeKeyPairs" )) describeKeyPairs(request, response); + else if (action.equalsIgnoreCase( "GetPasswordData" )) getPasswordData(request, response); + else { + logger.error("Unsupported action " + action); + throw new EC2ServiceException(ClientError.Unsupported, "This operation is not available"); + } - if (action.equalsIgnoreCase( "AllocateAddress" )) allocateAddress(request, response); - else if (action.equalsIgnoreCase( "AssociateAddress" )) associateAddress(request, response); - else if (action.equalsIgnoreCase( "AttachVolume" )) attachVolume(request, response ); - else if (action.equalsIgnoreCase( "AuthorizeSecurityGroupIngress" )) authorizeSecurityGroupIngress(request, response); - else if (action.equalsIgnoreCase( "CreateImage" )) createImage(request, response); - else if (action.equalsIgnoreCase( "CreateSecurityGroup" )) createSecurityGroup(request, response); - else if (action.equalsIgnoreCase( "CreateSnapshot" )) createSnapshot(request, response); - else if (action.equalsIgnoreCase( "CreateVolume" )) createVolume(request, response); - else if (action.equalsIgnoreCase( "DeleteSecurityGroup" )) deleteSecurityGroup(request, response); - else if (action.equalsIgnoreCase( "DeleteSnapshot" )) deleteSnapshot(request, response); - else if (action.equalsIgnoreCase( "DeleteVolume" )) deleteVolume(request, response); - else if (action.equalsIgnoreCase( "DeregisterImage" )) deregisterImage(request, response); - else if (action.equalsIgnoreCase( "DescribeAddresses" )) describeAddresses(request, response); - else if (action.equalsIgnoreCase( "DescribeAvailabilityZones" )) describeAvailabilityZones(request, response); - else if (action.equalsIgnoreCase( "DescribeImageAttribute" )) describeImageAttribute(request, response); - else if (action.equalsIgnoreCase( "DescribeImages" )) describeImages(request, response); - else if (action.equalsIgnoreCase( "DescribeInstanceAttribute" )) describeInstanceAttribute(request, response); - else if (action.equalsIgnoreCase( "DescribeInstances" )) describeInstances(request, response); - else if (action.equalsIgnoreCase( "DescribeSecurityGroups" )) describeSecurityGroups(request, response); - else if (action.equalsIgnoreCase( "DescribeSnapshots" )) describeSnapshots(request, response); - else if (action.equalsIgnoreCase( "DescribeVolumes" )) describeVolumes(request, response); - else if (action.equalsIgnoreCase( "DetachVolume" )) detachVolume(request, response); - else if (action.equalsIgnoreCase( "DisassociateAddress" )) disassociateAddress(request, response); - else if (action.equalsIgnoreCase( "ModifyImageAttribute" )) modifyImageAttribute(request, response); - else if (action.equalsIgnoreCase( "RebootInstances" )) rebootInstances(request, response); - else if (action.equalsIgnoreCase( "RegisterImage" )) registerImage(request, response); - else if (action.equalsIgnoreCase( "ReleaseAddress" )) releaseAddress(request, response); - else if (action.equalsIgnoreCase( "ResetImageAttribute" )) resetImageAttribute(request, response); - else if (action.equalsIgnoreCase( "RevokeSecurityGroupIngress")) revokeSecurityGroupIngress(request, response); - else if (action.equalsIgnoreCase( "RunInstances" )) runInstances(request, response); - else if (action.equalsIgnoreCase( "StartInstances" )) startInstances(request, response); - else if (action.equalsIgnoreCase( "StopInstances" )) stopInstances(request, response); - else if (action.equalsIgnoreCase( "TerminateInstances" )) terminateInstances(request, response); - else if (action.equalsIgnoreCase( "SetCertificate" )) setCertificate(request, response); - else if (action.equalsIgnoreCase( "DeleteCertificate" )) deleteCertificate(request, response); - else if (action.equalsIgnoreCase( "SetOfferMapping" )) setOfferMapping(request, response); - else if (action.equalsIgnoreCase( "DeleteOfferMapping" )) deleteOfferMapping(request, response); - else if (action.equalsIgnoreCase( "CreateKeyPair" )) createKeyPair(request, response); - else if (action.equalsIgnoreCase( "ImportKeyPair" )) importKeyPair(request, response); - else if (action.equalsIgnoreCase( "DeleteKeyPair" )) deleteKeyPair(request, response); - else if (action.equalsIgnoreCase( "DescribeKeyPairs" )) describeKeyPairs(request, response); - else if (action.equalsIgnoreCase( "GetPasswordData" )) getPasswordData(request, response); - else { - logger.error("Unsupported action " + action); - throw new EC2ServiceException(ClientError.Unsupported, "This operation is not available"); - } - } catch( EC2ServiceException e ) { - response.setStatus(e.getErrorCode()); - - if (e.getCause() != null && e.getCause() instanceof AxisFault) - faultResponse(response, ((AxisFault)e.getCause()).getFaultCode().getLocalPart(), e.getMessage()); - else { - logger.error("EC2ServiceException: " + e.getMessage(), e); - endResponse(response, e.toString()); - } + response.setStatus(e.getErrorCode()); + + if (e.getCause() != null && e.getCause() instanceof AxisFault) + faultResponse(response, ((AxisFault)e.getCause()).getFaultCode().getLocalPart(), e.getMessage()); + else { + logger.error("EC2ServiceException: " + e.getMessage(), e); + endResponse(response, e.toString()); + } } catch( PermissionDeniedException e ) { - logger.error("Unexpected exception: " + e.getMessage(), e); - response.setStatus(403); - endResponse(response, "Access denied"); - + logger.error("Unexpected exception: " + e.getMessage(), e); + response.setStatus(403); + endResponse(response, "Access denied"); + } catch( Exception e ) { - logger.error("Unexpected exception: " + e.getMessage(), e); - response.setStatus(500); - endResponse(response, e.toString()); - + logger.error("Unexpected exception: " + e.getMessage(), e); + response.setStatus(500); + endResponse(response, e.toString()); + } finally { - try { - response.flushBuffer(); - } catch (IOException e) { - logger.error("Unexpected exception " + e.getMessage(), e); - } + try { + response.flushBuffer(); + } catch (IOException e) { + logger.error("Unexpected exception " + e.getMessage(), e); + } } } - + /** * Provide an easy way to determine the version of the implementation running. * @@ -320,7 +320,7 @@ public class EC2RestServlet extends HttpServlet { response.setStatus(200); endResponse(response, version_response); } - + /** * This request registers the Cloud.com account holder to the EC2 service. The Cloud.com * account holder saves his API access and secret keys with the EC2 service so that @@ -340,54 +340,54 @@ public class EC2RestServlet extends HttpServlet { * As with all REST calls HTTPS should be used to ensure their security. */ private void setUserKeys( HttpServletRequest request, HttpServletResponse response ) { - String[] accessKey = null; - String[] secretKey = null; - Transaction txn = null; - try { - // -> all these parameters are required + String[] accessKey = null; + String[] secretKey = null; + Transaction txn = null; + try { + // -> all these parameters are required accessKey = request.getParameterValues( "accesskey" ); - if ( null == accessKey || 0 == accessKey.length ) { - response.sendError(530, "Missing accesskey parameter" ); - return; - } + if ( null == accessKey || 0 == accessKey.length ) { + response.sendError(530, "Missing accesskey parameter" ); + return; + } secretKey = request.getParameterValues( "secretkey" ); if ( null == secretKey || 0 == secretKey.length ) { - response.sendError(530, "Missing secretkey parameter" ); - return; + response.sendError(530, "Missing secretkey parameter" ); + return; } } catch( Exception e ) { - logger.error("SetUserKeys exception " + e.getMessage(), e); - response.setStatus(500); - endResponse(response, "SetUserKeys exception " + e.getMessage()); - return; + logger.error("SetUserKeys exception " + e.getMessage(), e); + response.setStatus(500); + endResponse(response, "SetUserKeys exception " + e.getMessage()); + return; } - - // prime UserContext here + + // prime UserContext here // logger.debug("initializing context"); - UserContext context = UserContext.current(); + UserContext context = UserContext.current(); try { txn = Transaction.open(Transaction.AWSAPI_DB); // -> use the keys to see if the account actually exists - ServiceProvider.getInstance().getEC2Engine().validateAccount( accessKey[0], secretKey[0] ); -/* UserCredentialsDao credentialDao = new UserCredentialsDao(); + ServiceProvider.getInstance().getEC2Engine().validateAccount( accessKey[0], secretKey[0] ); + /* UserCredentialsDao credentialDao = new UserCredentialsDao(); credentialDao.setUserKeys( ); -*/ UserCredentialsVO user = new UserCredentialsVO(accessKey[0], secretKey[0]); - ucDao.persist(user); - txn.commit(); - + */ UserCredentialsVO user = new UserCredentialsVO(accessKey[0], secretKey[0]); + ucDao.persist(user); + txn.commit(); + } catch( Exception e ) { - logger.error("SetUserKeys " + e.getMessage(), e); - response.setStatus(401); - endResponse(response, e.toString()); - txn.close(); - return; + logger.error("SetUserKeys " + e.getMessage(), e); + response.setStatus(401); + endResponse(response, e.toString()); + txn.close(); + return; } - response.setStatus(200); + response.setStatus(200); endResponse(response, "User keys set successfully"); } - + /** * The SOAP API for EC2 uses WS-Security to sign all client requests. This requires that * the client have a public/private key pair and the public key defined by a X509 certificate. @@ -405,46 +405,46 @@ public class EC2RestServlet extends HttpServlet { * simply over writes any previously stored value. */ private void setCertificate( HttpServletRequest request, HttpServletResponse response ) - throws Exception { + throws Exception { Transaction txn = null; - try { - // [A] Pull the cert and cloud AccessKey from the request + try { + // [A] Pull the cert and cloud AccessKey from the request String[] certificate = request.getParameterValues( "cert" ); - if (null == certificate || 0 == certificate.length) { - response.sendError(530, "Missing cert parameter" ); - return; - } + if (null == certificate || 0 == certificate.length) { + response.sendError(530, "Missing cert parameter" ); + return; + } // logger.debug( "SetCertificate cert: [" + certificate[0] + "]" ); - + String [] accessKey = request.getParameterValues( "AWSAccessKeyId" ); - if ( null == accessKey || 0 == accessKey.length ) { - response.sendError(530, "Missing AWSAccessKeyId parameter" ); - return; - } + if ( null == accessKey || 0 == accessKey.length ) { + response.sendError(530, "Missing AWSAccessKeyId parameter" ); + return; + } - // [B] Open our keystore - FileInputStream fsIn = new FileInputStream( pathToKeystore ); - KeyStore certStore = KeyStore.getInstance( "JKS" ); - certStore.load( fsIn, keystorePassword.toCharArray()); - - // -> use the Cloud API key to save the cert in the keystore - // -> write the cert into the keystore on disk - Certificate userCert = null; - CertificateFactory cf = CertificateFactory.getInstance( "X.509" ); + // [B] Open our keystore + FileInputStream fsIn = new FileInputStream( pathToKeystore ); + KeyStore certStore = KeyStore.getInstance( "JKS" ); + certStore.load( fsIn, keystorePassword.toCharArray()); - ByteArrayInputStream bs = new ByteArrayInputStream( certificate[0].getBytes()); - while (bs.available() > 0) userCert = cf.generateCertificate(bs); - certStore.setCertificateEntry( accessKey[0], userCert ); + // -> use the Cloud API key to save the cert in the keystore + // -> write the cert into the keystore on disk + Certificate userCert = null; + CertificateFactory cf = CertificateFactory.getInstance( "X.509" ); - FileOutputStream fsOut = new FileOutputStream( pathToKeystore ); - certStore.store( fsOut, keystorePassword.toCharArray()); - - // [C] Associate the cert's uniqueId with the Cloud API keys + ByteArrayInputStream bs = new ByteArrayInputStream( certificate[0].getBytes()); + while (bs.available() > 0) userCert = cf.generateCertificate(bs); + certStore.setCertificateEntry( accessKey[0], userCert ); + + FileOutputStream fsOut = new FileOutputStream( pathToKeystore ); + certStore.store( fsOut, keystorePassword.toCharArray()); + + // [C] Associate the cert's uniqueId with the Cloud API keys String uniqueId = AuthenticationUtils.X509CertUniqueId( userCert ); logger.debug( "SetCertificate, uniqueId: " + uniqueId ); -/* UserCredentialsDao credentialDao = new UserCredentialsDao(); + /* UserCredentialsDao credentialDao = new UserCredentialsDao(); credentialDao.setCertificateId( accessKey[0], uniqueId ); -*/ + */ txn = Transaction.open(Transaction.AWSAPI_DB); UserCredentialsVO user = ucDao.getByAccessKey(accessKey[0]); user.setCertUniqueId(uniqueId); @@ -452,20 +452,20 @@ public class EC2RestServlet extends HttpServlet { response.setStatus(200); endResponse(response, "User certificate set successfully"); txn.commit(); - - } catch( NoSuchObjectException e ) { - logger.error("SetCertificate exception " + e.getMessage(), e); - response.sendError(404, "SetCertificate exception " + e.getMessage()); - + + } catch( NoSuchObjectException e ) { + logger.error("SetCertificate exception " + e.getMessage(), e); + response.sendError(404, "SetCertificate exception " + e.getMessage()); + } catch( Exception e ) { - logger.error("SetCertificate exception " + e.getMessage(), e); - response.sendError(500, "SetCertificate exception " + e.getMessage()); + logger.error("SetCertificate exception " + e.getMessage(), e); + response.sendError(500, "SetCertificate exception " + e.getMessage()); } finally { txn.close(); } - + } - + /** * The SOAP API for EC2 uses WS-Security to sign all client requests. This requires that * the client have a public/private key pair and the public key defined by a X509 certificate. @@ -478,133 +478,133 @@ public class EC2RestServlet extends HttpServlet { * algorithm. */ private void deleteCertificate( HttpServletRequest request, HttpServletResponse response ) - throws Exception { + throws Exception { Transaction txn = null; - try { + try { String [] accessKey = request.getParameterValues( "AWSAccessKeyId" ); - if ( null == accessKey || 0 == accessKey.length ) { - response.sendError(530, "Missing AWSAccessKeyId parameter" ); - return; - } + if ( null == accessKey || 0 == accessKey.length ) { + response.sendError(530, "Missing AWSAccessKeyId parameter" ); + return; + } - // -> delete the specified entry and save back to disk - FileInputStream fsIn = new FileInputStream( pathToKeystore ); - KeyStore certStore = KeyStore.getInstance( "JKS" ); - certStore.load( fsIn, keystorePassword.toCharArray()); + // -> delete the specified entry and save back to disk + FileInputStream fsIn = new FileInputStream( pathToKeystore ); + KeyStore certStore = KeyStore.getInstance( "JKS" ); + certStore.load( fsIn, keystorePassword.toCharArray()); - if ( certStore.containsAlias( accessKey[0] )) { - certStore.deleteEntry( accessKey[0] ); - FileOutputStream fsOut = new FileOutputStream( pathToKeystore ); - certStore.store( fsOut, keystorePassword.toCharArray()); - - // -> dis-associate the cert's uniqueId with the Cloud API keys -/* UserCredentialsDao credentialDao = new UserCredentialsDao(); + if ( certStore.containsAlias( accessKey[0] )) { + certStore.deleteEntry( accessKey[0] ); + FileOutputStream fsOut = new FileOutputStream( pathToKeystore ); + certStore.store( fsOut, keystorePassword.toCharArray()); + + // -> dis-associate the cert's uniqueId with the Cloud API keys + /* UserCredentialsDao credentialDao = new UserCredentialsDao(); credentialDao.setCertificateId( accessKey[0], null ); - -*/ txn = Transaction.open(Transaction.AWSAPI_DB); - UserCredentialsVO user = ucDao.getByAccessKey(accessKey[0]); - user.setCertUniqueId(null); - ucDao.update(user.getId(), user); - response.setStatus(200); - endResponse(response, "User certificate deleted successfully"); - txn.commit(); - } - else response.setStatus(404); - - } catch( NoSuchObjectException e ) { - logger.error("SetCertificate exception " + e.getMessage(), e); - response.sendError(404, "SetCertificate exception " + e.getMessage()); + + */ txn = Transaction.open(Transaction.AWSAPI_DB); + UserCredentialsVO user = ucDao.getByAccessKey(accessKey[0]); + user.setCertUniqueId(null); + ucDao.update(user.getId(), user); + response.setStatus(200); + endResponse(response, "User certificate deleted successfully"); + txn.commit(); + } + else response.setStatus(404); + + } catch( NoSuchObjectException e ) { + logger.error("SetCertificate exception " + e.getMessage(), e); + response.sendError(404, "SetCertificate exception " + e.getMessage()); } catch( Exception e ) { - logger.error("DeleteCertificate exception " + e.getMessage(), e); - response.sendError(500, "DeleteCertificate exception " + e.getMessage()); + logger.error("DeleteCertificate exception " + e.getMessage(), e); + response.sendError(500, "DeleteCertificate exception " + e.getMessage()); } finally { txn.close(); } } - + /** * Allow the caller to define the mapping between the Amazon instance type strings * (e.g., m1.small, cc1.4xlarge) and the cloudstack service offering ids. Setting * an existing mapping just over writes the prevous values. */ private void setOfferMapping( HttpServletRequest request, HttpServletResponse response ) { - String amazonOffer = null; - String cloudOffer = null; - - try { - // -> all these parameters are required + String amazonOffer = null; + String cloudOffer = null; + + try { + // -> all these parameters are required amazonOffer = request.getParameter( "amazonoffer" ); - if ( null == amazonOffer ) { - response.sendError(530, "Missing amazonoffer parameter" ); - return; - } + if ( null == amazonOffer ) { + response.sendError(530, "Missing amazonoffer parameter" ); + return; + } cloudOffer = request.getParameter( "cloudoffer" ); if ( null == cloudOffer ) { - response.sendError(530, "Missing cloudoffer parameter" ); - return; + response.sendError(530, "Missing cloudoffer parameter" ); + return; } } catch( Exception e ) { - logger.error("SetOfferMapping exception " + e.getMessage(), e); - response.setStatus(500); - endResponse(response, "SetOfferMapping exception " + e.getMessage()); - return; + logger.error("SetOfferMapping exception " + e.getMessage(), e); + response.setStatus(500); + endResponse(response, "SetOfferMapping exception " + e.getMessage()); + return; + } + + // validate account is admin level + try { + CloudStackAccount currentAccount = ServiceProvider.getInstance().getEC2Engine().getCurrentAccount(); + + if (currentAccount.getAccountType() != 1) { + logger.debug("SetOfferMapping called by non-admin user!"); + response.setStatus(500); + endResponse(response, "Permission denied for non-admin user to setOfferMapping!"); + return; + } + } catch (Exception e) { + logger.error("SetOfferMapping " + e.getMessage(), e); + response.setStatus(401); + endResponse(response, e.toString()); + return; } - - // validate account is admin level - try { - CloudStackAccount currentAccount = ServiceProvider.getInstance().getEC2Engine().getCurrentAccount(); - - if (currentAccount.getAccountType() != 1) { - logger.debug("SetOfferMapping called by non-admin user!"); - response.setStatus(500); - endResponse(response, "Permission denied for non-admin user to setOfferMapping!"); - return; - } - } catch (Exception e) { - logger.error("SetOfferMapping " + e.getMessage(), e); - response.setStatus(401); - endResponse(response, e.toString()); - return; - } try { - - ofDao.setOfferMapping( amazonOffer, cloudOffer ); - + + ofDao.setOfferMapping( amazonOffer, cloudOffer ); + } catch( Exception e ) { - logger.error("SetOfferMapping " + e.getMessage(), e); - response.setStatus(401); - endResponse(response, e.toString()); - return; + logger.error("SetOfferMapping " + e.getMessage(), e); + response.setStatus(401); + endResponse(response, e.toString()); + return; } - response.setStatus(200); + response.setStatus(200); endResponse(response, "offering mapping set successfully"); } private void deleteOfferMapping( HttpServletRequest request, HttpServletResponse response ) { - String amazonOffer = null; - - try { - // -> all these parameters are required + String amazonOffer = null; + + try { + // -> all these parameters are required amazonOffer = request.getParameter( "amazonoffer" ); - if ( null == amazonOffer ) { - response.sendError(530, "Missing amazonoffer parameter" ); - return; - } + if ( null == amazonOffer ) { + response.sendError(530, "Missing amazonoffer parameter" ); + return; + } } catch( Exception e ) { - logger.error("DeleteOfferMapping exception " + e.getMessage(), e); - response.setStatus(500); - endResponse(response, "DeleteOfferMapping exception " + e.getMessage()); - return; + logger.error("DeleteOfferMapping exception " + e.getMessage(), e); + response.setStatus(500); + endResponse(response, "DeleteOfferMapping exception " + e.getMessage()); + return; } - - // validate account is admin level - try { + + // validate account is admin level + try { CloudStackAccount currentAccount = ServiceProvider.getInstance().getEC2Engine().getCurrentAccount(); - + if (currentAccount.getAccountType() != 1) { logger.debug("deleteOfferMapping called by non-admin user!"); response.setStatus(500); @@ -619,14 +619,14 @@ public class EC2RestServlet extends HttpServlet { } try { - ofDao.deleteOfferMapping( amazonOffer ); + ofDao.deleteOfferMapping( amazonOffer ); } catch( Exception e ) { - logger.error("DeleteOfferMapping " + e.getMessage(), e); - response.setStatus(401); - endResponse(response, e.toString()); - return; + logger.error("DeleteOfferMapping " + e.getMessage(), e); + response.setStatus(401); + endResponse(response, e.toString()); + return; } - response.setStatus(200); + response.setStatus(200); endResponse(response, "offering mapping deleted successfully"); } @@ -641,257 +641,257 @@ public class EC2RestServlet extends HttpServlet { * response XML. */ private void attachVolume( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2Volume EC2request = new EC2Volume(); - - // -> all these parameters are required + throws ADBException, XMLStreamException, IOException { + EC2Volume EC2request = new EC2Volume(); + + // -> all these parameters are required String[] volumeId = request.getParameterValues( "VolumeId" ); - if ( null != volumeId && 0 < volumeId.length ) - EC2request.setId( volumeId[0] ); - else { response.sendError(530, "Missing VolumeId parameter" ); return; } + if ( null != volumeId && 0 < volumeId.length ) + EC2request.setId( volumeId[0] ); + else { response.sendError(530, "Missing VolumeId parameter" ); return; } String[] instanceId = request.getParameterValues( "InstanceId" ); if ( null != instanceId && 0 < instanceId.length ) - EC2request.setInstanceId( instanceId[0] ); - else { response.sendError(530, "Missing InstanceId parameter" ); return; } + EC2request.setInstanceId( instanceId[0] ); + else { response.sendError(530, "Missing InstanceId parameter" ); return; } String[] device = request.getParameterValues( "Device" ); if ( null != device && 0 < device.length ) - EC2request.setDevice( device[0] ); - else { response.sendError(530, "Missing Device parameter" ); return; } - - // -> execute the request - AttachVolumeResponse EC2response = EC2SoapServiceImpl.toAttachVolumeResponse( ServiceProvider.getInstance().getEC2Engine().attachVolume( EC2request )); - serializeResponse(response, EC2response); + EC2request.setDevice( device[0] ); + else { response.sendError(530, "Missing Device parameter" ); return; } + + // -> execute the request + AttachVolumeResponse EC2response = EC2SoapServiceImpl.toAttachVolumeResponse( ServiceProvider.getInstance().getEC2Engine().attachVolume( EC2request )); + serializeResponse(response, EC2response); } - + /** * The SOAP equivalent of this function appears to allow multiple permissions per request, yet * in the REST API documentation only one permission is allowed. */ private void revokeSecurityGroupIngress( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { + throws ADBException, XMLStreamException, IOException { EC2AuthorizeRevokeSecurityGroup EC2request = new EC2AuthorizeRevokeSecurityGroup(); String[] groupName = request.getParameterValues( "GroupName" ); - if ( null != groupName && 0 < groupName.length ) - EC2request.setName( groupName[0] ); - else { response.sendError(530, "Missing GroupName parameter" ); return; } + if ( null != groupName && 0 < groupName.length ) + EC2request.setName( groupName[0] ); + else { response.sendError(530, "Missing GroupName parameter" ); return; } - EC2IpPermission perm = new EC2IpPermission(); + EC2IpPermission perm = new EC2IpPermission(); String[] protocol = request.getParameterValues( "IpProtocol" ); - if ( null != protocol && 0 < protocol.length ) - perm.setProtocol( protocol[0] ); - else { response.sendError(530, "Missing IpProtocol parameter" ); return; } + if ( null != protocol && 0 < protocol.length ) + perm.setProtocol( protocol[0] ); + else { response.sendError(530, "Missing IpProtocol parameter" ); return; } String[] fromPort = request.getParameterValues( "FromPort" ); - if ( null != fromPort && 0 < fromPort.length ) - perm.setProtocol( fromPort[0] ); - else { response.sendError(530, "Missing FromPort parameter" ); return; } + if ( null != fromPort && 0 < fromPort.length ) + perm.setProtocol( fromPort[0] ); + else { response.sendError(530, "Missing FromPort parameter" ); return; } String[] toPort = request.getParameterValues( "ToPort" ); - if ( null != toPort && 0 < toPort.length ) - perm.setProtocol( toPort[0] ); - else { response.sendError(530, "Missing ToPort parameter" ); return; } - - String[] ranges = request.getParameterValues( "CidrIp" ); - if ( null != ranges && 0 < ranges.length) - perm.addIpRange( ranges[0] ); - else { response.sendError(530, "Missing CidrIp parameter" ); return; } - - String[] user = request.getParameterValues( "SourceSecurityGroupOwnerId" ); - if ( null == user || 0 == user.length) { - response.sendError(530, "Missing SourceSecurityGroupOwnerId parameter" ); - return; - } - - String[] name = request.getParameterValues( "SourceSecurityGroupName" ); - if ( null == name || 0 == name.length) { - response.sendError(530, "Missing SourceSecurityGroupName parameter" ); - return; - } + if ( null != toPort && 0 < toPort.length ) + perm.setProtocol( toPort[0] ); + else { response.sendError(530, "Missing ToPort parameter" ); return; } - EC2SecurityGroup group = new EC2SecurityGroup(); - group.setAccount( user[0] ); - group.setName( name[0] ); - perm.addUser( group ); - EC2request.addIpPermission( perm ); - - // -> execute the request + String[] ranges = request.getParameterValues( "CidrIp" ); + if ( null != ranges && 0 < ranges.length) + perm.addIpRange( ranges[0] ); + else { response.sendError(530, "Missing CidrIp parameter" ); return; } + + String[] user = request.getParameterValues( "SourceSecurityGroupOwnerId" ); + if ( null == user || 0 == user.length) { + response.sendError(530, "Missing SourceSecurityGroupOwnerId parameter" ); + return; + } + + String[] name = request.getParameterValues( "SourceSecurityGroupName" ); + if ( null == name || 0 == name.length) { + response.sendError(530, "Missing SourceSecurityGroupName parameter" ); + return; + } + + EC2SecurityGroup group = new EC2SecurityGroup(); + group.setAccount( user[0] ); + group.setName( name[0] ); + perm.addUser( group ); + EC2request.addIpPermission( perm ); + + // -> execute the request RevokeSecurityGroupIngressResponse EC2response = EC2SoapServiceImpl.toRevokeSecurityGroupIngressResponse( - ServiceProvider.getInstance().getEC2Engine().revokeSecurityGroup( EC2request )); + ServiceProvider.getInstance().getEC2Engine().revokeSecurityGroup( EC2request )); serializeResponse(response, EC2response); } - private void authorizeSecurityGroupIngress( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - // -> parse the complicated paramters into our standard object + private void authorizeSecurityGroupIngress( HttpServletRequest request, HttpServletResponse response ) + throws ADBException, XMLStreamException, IOException { + // -> parse the complicated paramters into our standard object EC2AuthorizeRevokeSecurityGroup EC2request = new EC2AuthorizeRevokeSecurityGroup(); String[] groupName = request.getParameterValues( "GroupName" ); - if ( null != groupName && 0 < groupName.length ) - EC2request.setName( groupName[0] ); - else { response.sendError(530, "Missing GroupName parameter" ); return; } + if ( null != groupName && 0 < groupName.length ) + EC2request.setName( groupName[0] ); + else { response.sendError(530, "Missing GroupName parameter" ); return; } - // -> not clear how many parameters there are until we fail to get IpPermissions.n.IpProtocol - int nCount = 1; - do - { EC2IpPermission perm = new EC2IpPermission(); + // -> not clear how many parameters there are until we fail to get IpPermissions.n.IpProtocol + int nCount = 1; + do + { EC2IpPermission perm = new EC2IpPermission(); - String[] protocol = request.getParameterValues( "IpPermissions." + nCount + ".IpProtocol" ); - if ( null != protocol && 0 < protocol.length ) - perm.setProtocol( protocol[0] ); - else break; + String[] protocol = request.getParameterValues( "IpPermissions." + nCount + ".IpProtocol" ); + if ( null != protocol && 0 < protocol.length ) + perm.setProtocol( protocol[0] ); + else break; - String[] fromPort = request.getParameterValues( "IpPermissions." + nCount + ".FromPort" ); - if (null != fromPort && 0 < fromPort.length) perm.setProtocol( fromPort[0] ); + String[] fromPort = request.getParameterValues( "IpPermissions." + nCount + ".FromPort" ); + if (null != fromPort && 0 < fromPort.length) perm.setProtocol( fromPort[0] ); - String[] toPort = request.getParameterValues( "IpPermissions." + nCount + ".ToPort" ); - if (null != toPort && 0 < toPort.length) perm.setProtocol( toPort[0] ); - - // -> list: IpPermissions.n.IpRanges.m.CidrIp - int mCount = 1; - do - { String[] ranges = request.getParameterValues( "IpPermissions." + nCount + ".IpRanges." + mCount + ".CidrIp" ); - if ( null != ranges && 0 < ranges.length) - perm.addIpRange( ranges[0] ); - else break; - mCount++; - - } while( true ); + String[] toPort = request.getParameterValues( "IpPermissions." + nCount + ".ToPort" ); + if (null != toPort && 0 < toPort.length) perm.setProtocol( toPort[0] ); - // -> list: IpPermissions.n.Groups.m.UserId and IpPermissions.n.Groups.m.GroupName - mCount = 1; - do - { String[] user = request.getParameterValues( "IpPermissions." + nCount + ".Groups." + mCount + ".UserId" ); - if ( null == user || 0 == user.length) break; - - String[] name = request.getParameterValues( "IpPermissions." + nCount + ".Groups." + mCount + ".GroupName" ); - if ( null == name || 0 == name.length) break; + // -> list: IpPermissions.n.IpRanges.m.CidrIp + int mCount = 1; + do + { String[] ranges = request.getParameterValues( "IpPermissions." + nCount + ".IpRanges." + mCount + ".CidrIp" ); + if ( null != ranges && 0 < ranges.length) + perm.addIpRange( ranges[0] ); + else break; + mCount++; - EC2SecurityGroup group = new EC2SecurityGroup(); - group.setAccount( user[0] ); - group.setName( name[0] ); - perm.addUser( group ); - mCount++; - - } while( true ); - - // -> multiple IP permissions can be specified per group name - EC2request.addIpPermission( perm ); - nCount++; - - } while( true ); - - if (1 == nCount) { response.sendError(530, "At least one IpPermissions required" ); return; } + } while( true ); - - // -> execute the request + // -> list: IpPermissions.n.Groups.m.UserId and IpPermissions.n.Groups.m.GroupName + mCount = 1; + do + { String[] user = request.getParameterValues( "IpPermissions." + nCount + ".Groups." + mCount + ".UserId" ); + if ( null == user || 0 == user.length) break; + + String[] name = request.getParameterValues( "IpPermissions." + nCount + ".Groups." + mCount + ".GroupName" ); + if ( null == name || 0 == name.length) break; + + EC2SecurityGroup group = new EC2SecurityGroup(); + group.setAccount( user[0] ); + group.setName( name[0] ); + perm.addUser( group ); + mCount++; + + } while( true ); + + // -> multiple IP permissions can be specified per group name + EC2request.addIpPermission( perm ); + nCount++; + + } while( true ); + + if (1 == nCount) { response.sendError(530, "At least one IpPermissions required" ); return; } + + + // -> execute the request AuthorizeSecurityGroupIngressResponse EC2response = EC2SoapServiceImpl.toAuthorizeSecurityGroupIngressResponse( - ServiceProvider.getInstance().getEC2Engine().authorizeSecurityGroup( EC2request )); + ServiceProvider.getInstance().getEC2Engine().authorizeSecurityGroup( EC2request )); serializeResponse(response, EC2response); } - + private void detachVolume( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2Volume EC2request = new EC2Volume(); - + throws ADBException, XMLStreamException, IOException { + EC2Volume EC2request = new EC2Volume(); + String[] volumeId = request.getParameterValues( "VolumeId" ); - if ( null != volumeId && 0 < volumeId.length ) - EC2request.setId(volumeId[0]); - else { response.sendError(530, "Missing VolumeId parameter" ); return; } + if ( null != volumeId && 0 < volumeId.length ) + EC2request.setId(volumeId[0]); + else { response.sendError(530, "Missing VolumeId parameter" ); return; } String[] instanceId = request.getParameterValues( "InstanceId" ); if ( null != instanceId && 0 < instanceId.length ) - EC2request.setInstanceId(instanceId[0]); + EC2request.setInstanceId(instanceId[0]); String[] device = request.getParameterValues( "Device" ); if ( null != device && 0 < device.length ) - EC2request.setDevice( device[0] ); - - // -> execute the request - DetachVolumeResponse EC2response = EC2SoapServiceImpl.toDetachVolumeResponse( ServiceProvider.getInstance().getEC2Engine().detachVolume( EC2request )); - serializeResponse(response, EC2response); + EC2request.setDevice( device[0] ); + + // -> execute the request + DetachVolumeResponse EC2response = EC2SoapServiceImpl.toDetachVolumeResponse( ServiceProvider.getInstance().getEC2Engine().detachVolume( EC2request )); + serializeResponse(response, EC2response); } private void deleteVolume( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2Volume EC2request = new EC2Volume(); - - String[] volumeId = request.getParameterValues( "VolumeId" ); - if ( null != volumeId && 0 < volumeId.length ) - EC2request.setId(volumeId[0]); - else { response.sendError(530, "Missing VolumeId parameter" ); return; } + throws ADBException, XMLStreamException, IOException { + EC2Volume EC2request = new EC2Volume(); - // -> execute the request - DeleteVolumeResponse EC2response = EC2SoapServiceImpl.toDeleteVolumeResponse( ServiceProvider.getInstance().getEC2Engine().deleteVolume( EC2request )); - serializeResponse(response, EC2response); + String[] volumeId = request.getParameterValues( "VolumeId" ); + if ( null != volumeId && 0 < volumeId.length ) + EC2request.setId(volumeId[0]); + else { response.sendError(530, "Missing VolumeId parameter" ); return; } + + // -> execute the request + DeleteVolumeResponse EC2response = EC2SoapServiceImpl.toDeleteVolumeResponse( ServiceProvider.getInstance().getEC2Engine().deleteVolume( EC2request )); + serializeResponse(response, EC2response); } private void createVolume( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2CreateVolume EC2request = new EC2CreateVolume(); - + throws ADBException, XMLStreamException, IOException { + EC2CreateVolume EC2request = new EC2CreateVolume(); + String[] zoneName = request.getParameterValues( "AvailabilityZone" ); if ( null != zoneName && 0 < zoneName.length ) - EC2request.setZoneName( zoneName[0] ); - else { response.sendError(530, "Missing AvailabilityZone parameter" ); return; } - + EC2request.setZoneName( zoneName[0] ); + else { response.sendError(530, "Missing AvailabilityZone parameter" ); return; } + String[] size = request.getParameterValues( "Size" ); String[] snapshotId = request.getParameterValues("SnapshotId"); boolean useSnapshot = false; boolean useSize = false; - - if (null != size && 0 < size.length) - useSize = true; - - if (snapshotId != null && snapshotId.length != 0) - useSnapshot = true; - - if (useSize && !useSnapshot) { - EC2request.setSize( size[0] ); - } else if (useSnapshot && !useSize) { - EC2request.setSnapshotId(snapshotId[0]); - } else if (useSize && useSnapshot) { - response.sendError(530, "Size and SnapshotId parameters are mutually exclusive" ); return; - } else { - response.sendError(530, "Size or SnapshotId has to be specified" ); return; - } - - // -> execute the request - CreateVolumeResponse EC2response = EC2SoapServiceImpl.toCreateVolumeResponse( ServiceProvider.getInstance().getEC2Engine().createVolume( EC2request )); - serializeResponse(response, EC2response); + if (null != size && 0 < size.length) + useSize = true; + + if (snapshotId != null && snapshotId.length != 0) + useSnapshot = true; + + if (useSize && !useSnapshot) { + EC2request.setSize( size[0] ); + } else if (useSnapshot && !useSize) { + EC2request.setSnapshotId(snapshotId[0]); + } else if (useSize && useSnapshot) { + response.sendError(530, "Size and SnapshotId parameters are mutually exclusive" ); return; + } else { + response.sendError(530, "Size or SnapshotId has to be specified" ); return; + } + + + // -> execute the request + CreateVolumeResponse EC2response = EC2SoapServiceImpl.toCreateVolumeResponse( ServiceProvider.getInstance().getEC2Engine().createVolume( EC2request )); + serializeResponse(response, EC2response); } private void createSecurityGroup( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - - String groupName, groupDescription = null; - + throws ADBException, XMLStreamException, IOException { + + String groupName, groupDescription = null; + String[] name = request.getParameterValues( "GroupName" ); - if ( null != name && 0 < name.length ) - groupName = name[0]; - else { response.sendError(530, "Missing GroupName parameter" ); return; } - + if ( null != name && 0 < name.length ) + groupName = name[0]; + else { response.sendError(530, "Missing GroupName parameter" ); return; } + String[] desc = request.getParameterValues( "GroupDescription" ); if ( null != desc && 0 < desc.length ) - groupDescription = desc[0]; - else { response.sendError(530, "Missing GroupDescription parameter" ); return; } + groupDescription = desc[0]; + else { response.sendError(530, "Missing GroupDescription parameter" ); return; } - // -> execute the request + // -> execute the request CreateSecurityGroupResponse EC2response = EC2SoapServiceImpl.toCreateSecurityGroupResponse( ServiceProvider.getInstance().getEC2Engine().createSecurityGroup( groupName, groupDescription )); serializeResponse(response, EC2response); } private void deleteSecurityGroup( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - String groupName = null; - + throws ADBException, XMLStreamException, IOException { + String groupName = null; + String[] name = request.getParameterValues( "GroupName" ); if ( null != name && 0 < name.length ) - groupName = name[0]; + groupName = name[0]; else { response.sendError(530, "Missing GroupName parameter" ); return; } // -> execute the request @@ -900,217 +900,217 @@ public class EC2RestServlet extends HttpServlet { } private void deleteSnapshot( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - String snapshotId = null; - + throws ADBException, XMLStreamException, IOException { + String snapshotId = null; + String[] snapSet = request.getParameterValues( "SnapshotId" ); - if ( null != snapSet && 0 < snapSet.length ) - snapshotId = snapSet[0]; - else { response.sendError(530, "Missing SnapshotId parameter" ); return; } - - // -> execute the request - DeleteSnapshotResponse EC2response = EC2SoapServiceImpl.toDeleteSnapshotResponse( ServiceProvider.getInstance().getEC2Engine().deleteSnapshot( snapshotId )); - serializeResponse(response, EC2response); + if ( null != snapSet && 0 < snapSet.length ) + snapshotId = snapSet[0]; + else { response.sendError(530, "Missing SnapshotId parameter" ); return; } + + // -> execute the request + DeleteSnapshotResponse EC2response = EC2SoapServiceImpl.toDeleteSnapshotResponse( ServiceProvider.getInstance().getEC2Engine().deleteSnapshot( snapshotId )); + serializeResponse(response, EC2response); } private void createSnapshot( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - String volumeId = null; - + throws ADBException, XMLStreamException, IOException { + String volumeId = null; + String[] volSet = request.getParameterValues( "VolumeId" ); - if ( null != volSet && 0 < volSet.length ) - volumeId = volSet[0]; - else { response.sendError(530, "Missing VolumeId parameter" ); return; } - - // -> execute the request - EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); + if ( null != volSet && 0 < volSet.length ) + volumeId = volSet[0]; + else { response.sendError(530, "Missing VolumeId parameter" ); return; } + + // -> execute the request + EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); CreateSnapshotResponse EC2response = EC2SoapServiceImpl.toCreateSnapshotResponse( engine.createSnapshot( volumeId ), engine); serializeResponse(response, EC2response); } - + private void deregisterImage( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2Image image = new EC2Image(); - + throws ADBException, XMLStreamException, IOException { + EC2Image image = new EC2Image(); + String[] imageId = request.getParameterValues( "ImageId" ); - if ( null != imageId && 0 < imageId.length ) - image.setId( imageId[0] ); - else { response.sendError(530, "Missing ImageId parameter" ); return; } - - // -> execute the request - DeregisterImageResponse EC2response = EC2SoapServiceImpl.toDeregisterImageResponse( ServiceProvider.getInstance().getEC2Engine().deregisterImage( image )); - serializeResponse(response, EC2response); + if ( null != imageId && 0 < imageId.length ) + image.setId( imageId[0] ); + else { response.sendError(530, "Missing ImageId parameter" ); return; } + + // -> execute the request + DeregisterImageResponse EC2response = EC2SoapServiceImpl.toDeregisterImageResponse( ServiceProvider.getInstance().getEC2Engine().deregisterImage( image )); + serializeResponse(response, EC2response); } - + private void createImage( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2CreateImage EC2request = new EC2CreateImage(); - + throws ADBException, XMLStreamException, IOException { + EC2CreateImage EC2request = new EC2CreateImage(); + String[] instanceId = request.getParameterValues( "InstanceId" ); - if ( null != instanceId && 0 < instanceId.length ) - EC2request.setInstanceId( instanceId[0] ); - else { response.sendError(530, "Missing InstanceId parameter" ); return; } - + if ( null != instanceId && 0 < instanceId.length ) + EC2request.setInstanceId( instanceId[0] ); + else { response.sendError(530, "Missing InstanceId parameter" ); return; } + String[] name = request.getParameterValues( "Name" ); if ( null != name && 0 < name.length ) - EC2request.setName( name[0] ); - else { response.sendError(530, "Missing Name parameter" ); return; } + EC2request.setName( name[0] ); + else { response.sendError(530, "Missing Name parameter" ); return; } String[] description = request.getParameterValues( "Description" ); if ( null != description && 0 < description.length ) - EC2request.setDescription( description[0] ); + EC2request.setDescription( description[0] ); - // -> execute the request + // -> execute the request CreateImageResponse EC2response = EC2SoapServiceImpl.toCreateImageResponse( ServiceProvider.getInstance().getEC2Engine().createImage( EC2request )); serializeResponse(response, EC2response); } private void registerImage( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2RegisterImage EC2request = new EC2RegisterImage(); - + throws ADBException, XMLStreamException, IOException { + EC2RegisterImage EC2request = new EC2RegisterImage(); + String[] location = request.getParameterValues( "ImageLocation" ); - if ( null != location && 0 < location.length ) - EC2request.setLocation( location[0] ); - else { response.sendError(530, "Missing ImageLocation parameter" ); return; } + if ( null != location && 0 < location.length ) + EC2request.setLocation( location[0] ); + else { response.sendError(530, "Missing ImageLocation parameter" ); return; } String[] cloudRedfined = request.getParameterValues( "Architecture" ); - if ( null != cloudRedfined && 0 < cloudRedfined.length ) - EC2request.setArchitecture( cloudRedfined[0] ); - else { response.sendError(530, "Missing Architecture parameter" ); return; } + if ( null != cloudRedfined && 0 < cloudRedfined.length ) + EC2request.setArchitecture( cloudRedfined[0] ); + else { response.sendError(530, "Missing Architecture parameter" ); return; } String[] name = request.getParameterValues( "Name" ); if ( null != name && 0 < name.length ) - EC2request.setName( name[0] ); + EC2request.setName( name[0] ); String[] description = request.getParameterValues( "Description" ); if ( null != description && 0 < description.length ) - EC2request.setDescription( description[0] ); + EC2request.setDescription( description[0] ); - // -> execute the request + // -> execute the request RegisterImageResponse EC2response = EC2SoapServiceImpl.toRegisterImageResponse( ServiceProvider.getInstance().getEC2Engine().registerImage( EC2request )); serializeResponse(response, EC2response); } private void modifyImageAttribute( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2Image image = new EC2Image(); - - // -> its interesting to note that the SOAP API docs has description but the REST API docs do not + throws ADBException, XMLStreamException, IOException { + EC2Image image = new EC2Image(); + + // -> its interesting to note that the SOAP API docs has description but the REST API docs do not String[] imageId = request.getParameterValues( "ImageId" ); - if ( null != imageId && 0 < imageId.length ) - image.setId( imageId[0] ); - else { response.sendError(530, "Missing ImageId parameter" ); return; } + if ( null != imageId && 0 < imageId.length ) + image.setId( imageId[0] ); + else { response.sendError(530, "Missing ImageId parameter" ); return; } String[] description = request.getParameterValues( "Description" ); - if ( null != description && 0 < description.length ) - image.setDescription( description[0] ); - else { response.sendError(530, "Missing Description parameter" ); return; } + if ( null != description && 0 < description.length ) + image.setDescription( description[0] ); + else { response.sendError(530, "Missing Description parameter" ); return; } - // -> execute the request - ModifyImageAttributeResponse EC2response = EC2SoapServiceImpl.toModifyImageAttributeResponse( ServiceProvider.getInstance().getEC2Engine().modifyImageAttribute( image )); - serializeResponse(response, EC2response); + // -> execute the request + ModifyImageAttributeResponse EC2response = EC2SoapServiceImpl.toModifyImageAttributeResponse( ServiceProvider.getInstance().getEC2Engine().modifyImageAttribute( image )); + serializeResponse(response, EC2response); } private void resetImageAttribute( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2Image image = new EC2Image(); - + throws ADBException, XMLStreamException, IOException { + EC2Image image = new EC2Image(); + String[] imageId = request.getParameterValues( "ImageId" ); - if ( null != imageId && 0 < imageId.length ) - image.setId( imageId[0] ); - else { response.sendError(530, "Missing ImageId parameter" ); return; } - - // -> execute the request - image.setDescription( "" ); - ResetImageAttributeResponse EC2response = EC2SoapServiceImpl.toResetImageAttributeResponse( ServiceProvider.getInstance().getEC2Engine().modifyImageAttribute( image )); - serializeResponse(response, EC2response); + if ( null != imageId && 0 < imageId.length ) + image.setId( imageId[0] ); + else { response.sendError(530, "Missing ImageId parameter" ); return; } + + // -> execute the request + image.setDescription( "" ); + ResetImageAttributeResponse EC2response = EC2SoapServiceImpl.toResetImageAttributeResponse( ServiceProvider.getInstance().getEC2Engine().modifyImageAttribute( image )); + serializeResponse(response, EC2response); } private void runInstances( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2RunInstances EC2request = new EC2RunInstances(); - - // -> so in the Amazon docs for this REST call there is no userData even though there is in the SOAP docs + throws ADBException, XMLStreamException, IOException { + EC2RunInstances EC2request = new EC2RunInstances(); + + // -> so in the Amazon docs for this REST call there is no userData even though there is in the SOAP docs String[] imageId = request.getParameterValues( "ImageId" ); - if ( null != imageId && 0 < imageId.length ) - EC2request.setTemplateId( imageId[0] ); - else { response.sendError(530, "Missing ImageId parameter" ); return; } + if ( null != imageId && 0 < imageId.length ) + EC2request.setTemplateId( imageId[0] ); + else { response.sendError(530, "Missing ImageId parameter" ); return; } String[] minCount = request.getParameterValues( "MinCount" ); - if ( null != minCount && 0 < minCount.length ) - EC2request.setMinCount( Integer.parseInt( minCount[0] )); - else { response.sendError(530, "Missing MinCount parameter" ); return; } + if ( null != minCount && 0 < minCount.length ) + EC2request.setMinCount( Integer.parseInt( minCount[0] )); + else { response.sendError(530, "Missing MinCount parameter" ); return; } String[] maxCount = request.getParameterValues( "MaxCount" ); - if ( null != maxCount && 0 < maxCount.length ) - EC2request.setMaxCount( Integer.parseInt( maxCount[0] )); - else { response.sendError(530, "Missing MaxCount parameter" ); return; } + if ( null != maxCount && 0 < maxCount.length ) + EC2request.setMaxCount( Integer.parseInt( maxCount[0] )); + else { response.sendError(530, "Missing MaxCount parameter" ); return; } String[] instanceType = request.getParameterValues( "InstanceType" ); - if ( null != instanceType && 0 < instanceType.length ) - EC2request.setInstanceType( instanceType[0] ); + if ( null != instanceType && 0 < instanceType.length ) + EC2request.setInstanceType( instanceType[0] ); String[] zoneName = request.getParameterValues( "Placement.AvailabilityZone" ); - if ( null != zoneName && 0 < zoneName.length ) - EC2request.setZoneName( zoneName[0] ); - - String[] size = request.getParameterValues("size"); - if (size != null) { - EC2request.setSize(Integer.valueOf(size[0])); - } + if ( null != zoneName && 0 < zoneName.length ) + EC2request.setZoneName( zoneName[0] ); - String[] keyName = request.getParameterValues("KeyName"); - if (keyName != null) { - EC2request.setKeyName(keyName[0]); - } - - // -> execute the request - EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); - RunInstancesResponse EC2response = EC2SoapServiceImpl.toRunInstancesResponse( engine.runInstances( EC2request ), engine); - serializeResponse(response, EC2response); + String[] size = request.getParameterValues("size"); + if (size != null) { + EC2request.setSize(Integer.valueOf(size[0])); + } + + String[] keyName = request.getParameterValues("KeyName"); + if (keyName != null) { + EC2request.setKeyName(keyName[0]); + } + + // -> execute the request + EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); + RunInstancesResponse EC2response = EC2SoapServiceImpl.toRunInstancesResponse( engine.runInstances( EC2request ), engine); + serializeResponse(response, EC2response); } private void rebootInstances( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { + throws ADBException, XMLStreamException, IOException { EC2RebootInstances EC2request = new EC2RebootInstances(); int count = 0; // -> load in all the "InstanceId.n" parameters if any - Enumeration names = request.getParameterNames(); + Enumeration names = request.getParameterNames(); while( names.hasMoreElements()) { String key = (String)names.nextElement(); if (key.startsWith("InstanceId")) { String[] value = request.getParameterValues( key ); if (null != value && 0 < value.length) { - EC2request.addInstanceId( value[0] ); - count++; + EC2request.addInstanceId( value[0] ); + count++; } } } if (0 == count) { response.sendError(530, "Missing InstanceId parameter" ); return; } - + // -> execute the request RebootInstancesResponse EC2response = EC2SoapServiceImpl.toRebootInstancesResponse( ServiceProvider.getInstance().getEC2Engine().rebootInstances(EC2request)); serializeResponse(response, EC2response); } private void startInstances( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { + throws ADBException, XMLStreamException, IOException { EC2StartInstances EC2request = new EC2StartInstances(); int count = 0; // -> load in all the "InstanceId.n" parameters if any - Enumeration names = request.getParameterNames(); + Enumeration names = request.getParameterNames(); while( names.hasMoreElements()) { - String key = (String)names.nextElement(); - if (key.startsWith("InstanceId")) { - String[] value = request.getParameterValues( key ); - if (null != value && 0 < value.length) { - EC2request.addInstanceId( value[0] ); - count++; - } - } + String key = (String)names.nextElement(); + if (key.startsWith("InstanceId")) { + String[] value = request.getParameterValues( key ); + if (null != value && 0 < value.length) { + EC2request.addInstanceId( value[0] ); + count++; + } + } } if (0 == count) { response.sendError(530, "Missing InstanceId parameter" ); return; } @@ -1120,50 +1120,50 @@ public class EC2RestServlet extends HttpServlet { } private void stopInstances( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2StopInstances EC2request = new EC2StopInstances(); - int count = 0; - - // -> load in all the "InstanceId.n" parameters if any - Enumeration names = request.getParameterNames(); - while( names.hasMoreElements()) { - String key = (String)names.nextElement(); - if (key.startsWith("InstanceId")) { - String[] value = request.getParameterValues( key ); - if (null != value && 0 < value.length) { - EC2request.addInstanceId( value[0] ); - count++; - } - } - } - if (0 == count) { response.sendError(530, "Missing InstanceId parameter" ); return; } - - // -> execute the request - StopInstancesResponse EC2response = EC2SoapServiceImpl.toStopInstancesResponse( ServiceProvider.getInstance().getEC2Engine().stopInstances( EC2request )); - serializeResponse(response, EC2response); - } - - private void terminateInstances( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { + throws ADBException, XMLStreamException, IOException { EC2StopInstances EC2request = new EC2StopInstances(); int count = 0; // -> load in all the "InstanceId.n" parameters if any - Enumeration names = request.getParameterNames(); + Enumeration names = request.getParameterNames(); while( names.hasMoreElements()) { - String key = (String)names.nextElement(); - if (key.startsWith("InstanceId")) { - String[] value = request.getParameterValues( key ); - if (null != value && 0 < value.length) { - EC2request.addInstanceId( value[0] ); - count++; - } - } + String key = (String)names.nextElement(); + if (key.startsWith("InstanceId")) { + String[] value = request.getParameterValues( key ); + if (null != value && 0 < value.length) { + EC2request.addInstanceId( value[0] ); + count++; + } + } + } + if (0 == count) { response.sendError(530, "Missing InstanceId parameter" ); return; } + + // -> execute the request + StopInstancesResponse EC2response = EC2SoapServiceImpl.toStopInstancesResponse( ServiceProvider.getInstance().getEC2Engine().stopInstances( EC2request )); + serializeResponse(response, EC2response); + } + + private void terminateInstances( HttpServletRequest request, HttpServletResponse response ) + throws ADBException, XMLStreamException, IOException { + EC2StopInstances EC2request = new EC2StopInstances(); + int count = 0; + + // -> load in all the "InstanceId.n" parameters if any + Enumeration names = request.getParameterNames(); + while( names.hasMoreElements()) { + String key = (String)names.nextElement(); + if (key.startsWith("InstanceId")) { + String[] value = request.getParameterValues( key ); + if (null != value && 0 < value.length) { + EC2request.addInstanceId( value[0] ); + count++; + } + } } if (0 == count) { response.sendError(530, "Missing InstanceId parameter" ); return; } // -> execute the request - EC2request.setDestroyInstances( true ); + EC2request.setDestroyInstances( true ); TerminateInstancesResponse EC2response = EC2SoapServiceImpl.toTermInstancesResponse( ServiceProvider.getInstance().getEC2Engine().stopInstances( EC2request )); serializeResponse(response, EC2response); } @@ -1173,100 +1173,100 @@ public class EC2RestServlet extends HttpServlet { * resulting EC2 Amazon object into XML to return to the client. */ private void describeAvailabilityZones( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2DescribeAvailabilityZones EC2request = new EC2DescribeAvailabilityZones(); - - // -> load in all the "ZoneName.n" parameters if any - Enumeration names = request.getParameterNames(); - while( names.hasMoreElements()) { - String key = (String)names.nextElement(); - if (key.startsWith("ZoneName")) { - String[] value = request.getParameterValues( key ); - if (null != value && 0 < value.length) EC2request.addZone( value[0] ); - } - } - // -> execute the request - DescribeAvailabilityZonesResponse EC2response = EC2SoapServiceImpl.toDescribeAvailabilityZonesResponse( ServiceProvider.getInstance().getEC2Engine().handleRequest( EC2request )); - serializeResponse(response, EC2response); + throws ADBException, XMLStreamException, IOException { + EC2DescribeAvailabilityZones EC2request = new EC2DescribeAvailabilityZones(); + + // -> load in all the "ZoneName.n" parameters if any + Enumeration names = request.getParameterNames(); + while( names.hasMoreElements()) { + String key = (String)names.nextElement(); + if (key.startsWith("ZoneName")) { + String[] value = request.getParameterValues( key ); + if (null != value && 0 < value.length) EC2request.addZone( value[0] ); + } + } + // -> execute the request + DescribeAvailabilityZonesResponse EC2response = EC2SoapServiceImpl.toDescribeAvailabilityZonesResponse( ServiceProvider.getInstance().getEC2Engine().handleRequest( EC2request )); + serializeResponse(response, EC2response); } private void describeImages( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2DescribeImages EC2request = new EC2DescribeImages(); - - // -> load in all the "ImageId.n" parameters if any, and ignore all other parameters - Enumeration names = request.getParameterNames(); - while( names.hasMoreElements()) { - String key = (String)names.nextElement(); - if (key.startsWith("ImageId")) { - String[] value = request.getParameterValues( key ); - if (null != value && 0 < value.length) EC2request.addImageSet( value[0] ); - } - } - // -> execute the request - EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); - DescribeImagesResponse EC2response = EC2SoapServiceImpl.toDescribeImagesResponse( engine.describeImages( EC2request )); - serializeResponse(response, EC2response); + throws ADBException, XMLStreamException, IOException { + EC2DescribeImages EC2request = new EC2DescribeImages(); + + // -> load in all the "ImageId.n" parameters if any, and ignore all other parameters + Enumeration names = request.getParameterNames(); + while( names.hasMoreElements()) { + String key = (String)names.nextElement(); + if (key.startsWith("ImageId")) { + String[] value = request.getParameterValues( key ); + if (null != value && 0 < value.length) EC2request.addImageSet( value[0] ); + } + } + // -> execute the request + EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); + DescribeImagesResponse EC2response = EC2SoapServiceImpl.toDescribeImagesResponse( engine.describeImages( EC2request )); + serializeResponse(response, EC2response); } - + private void describeImageAttribute( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2DescribeImages EC2request = new EC2DescribeImages(); - - // -> only works for queries about descriptions - String[] descriptions = request.getParameterValues( "Description" ); - if ( null != descriptions && 0 < descriptions.length ) { - String[] value = request.getParameterValues( "ImageId" ); - EC2request.addImageSet( value[0] ); - } - else { - response.sendError(501, "Unsupported - only description supported" ); - return; - } + throws ADBException, XMLStreamException, IOException { + EC2DescribeImages EC2request = new EC2DescribeImages(); - // -> execute the request - DescribeImageAttributeResponse EC2response = EC2SoapServiceImpl.toDescribeImageAttributeResponse( ServiceProvider.getInstance().getEC2Engine().describeImages( EC2request )); - serializeResponse(response, EC2response); + // -> only works for queries about descriptions + String[] descriptions = request.getParameterValues( "Description" ); + if ( null != descriptions && 0 < descriptions.length ) { + String[] value = request.getParameterValues( "ImageId" ); + EC2request.addImageSet( value[0] ); + } + else { + response.sendError(501, "Unsupported - only description supported" ); + return; + } + + // -> execute the request + DescribeImageAttributeResponse EC2response = EC2SoapServiceImpl.toDescribeImageAttributeResponse( ServiceProvider.getInstance().getEC2Engine().describeImages( EC2request )); + serializeResponse(response, EC2response); } - + private void describeInstances( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException - { - EC2DescribeInstances EC2request = new EC2DescribeInstances(); - - // -> load in all the "InstanceId.n" parameters if any - Enumeration names = request.getParameterNames(); - while( names.hasMoreElements()) - { - String key = (String)names.nextElement(); - if (key.startsWith("InstanceId")) { - String[] value = request.getParameterValues( key ); - if (null != value && 0 < value.length) EC2request.addInstanceId( value[0] ); - } - } - + throws ADBException, XMLStreamException, IOException + { + EC2DescribeInstances EC2request = new EC2DescribeInstances(); + + // -> load in all the "InstanceId.n" parameters if any + Enumeration names = request.getParameterNames(); + while( names.hasMoreElements()) + { + String key = (String)names.nextElement(); + if (key.startsWith("InstanceId")) { + String[] value = request.getParameterValues( key ); + if (null != value && 0 < value.length) EC2request.addInstanceId( value[0] ); + } + } + // -> are there any filters with this request? EC2Filter[] filterSet = extractFilters( request ); if (null != filterSet) { - EC2InstanceFilterSet ifs = new EC2InstanceFilterSet(); - for( int i=0; i < filterSet.length; i++ ) ifs.addFilter( filterSet[i] ); - EC2request.setFilterSet( ifs ); + EC2InstanceFilterSet ifs = new EC2InstanceFilterSet(); + for( int i=0; i < filterSet.length; i++ ) ifs.addFilter( filterSet[i] ); + EC2request.setFilterSet( ifs ); } - // -> execute the request - EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); - DescribeInstancesResponse EC2response = EC2SoapServiceImpl.toDescribeInstancesResponse( engine.describeInstances( EC2request ), engine); - serializeResponse(response, EC2response); - } - + // -> execute the request + EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); + DescribeInstancesResponse EC2response = EC2SoapServiceImpl.toDescribeInstancesResponse( engine.describeInstances( EC2request ), engine); + serializeResponse(response, EC2response); + } + private void describeAddresses( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { + throws ADBException, XMLStreamException, IOException { EC2DescribeAddresses ec2Request = new EC2DescribeAddresses(); // -> load in all the "PublicIp.n" parameters if any - Enumeration names = request.getParameterNames(); + Enumeration names = request.getParameterNames(); while( names.hasMoreElements()) { String key = (String)names.nextElement(); if (key.startsWith("PublicIp")) { @@ -1280,38 +1280,38 @@ public class EC2RestServlet extends HttpServlet { } private void allocateAddress( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - - EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); - - AllocateAddressResponse ec2Response = EC2SoapServiceImpl.toAllocateAddressResponse( engine.allocateAddress()); - - serializeResponse(response, ec2Response); + throws ADBException, XMLStreamException, IOException { + + EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); + + AllocateAddressResponse ec2Response = EC2SoapServiceImpl.toAllocateAddressResponse( engine.allocateAddress()); + + serializeResponse(response, ec2Response); } private void releaseAddress( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - - EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); + throws ADBException, XMLStreamException, IOException { - String publicIp = request.getParameter( "PublicIp" ); - if (publicIp == null) { - response.sendError(530, "Missing PublicIp parameter"); - return; - } - - EC2ReleaseAddress ec2Request = new EC2ReleaseAddress(); - if (ec2Request != null) { - ec2Request.setPublicIp(publicIp); - } - - ReleaseAddressResponse EC2Response = EC2SoapServiceImpl.toReleaseAddressResponse( engine.releaseAddress( ec2Request )); + EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); - serializeResponse(response, EC2Response); + String publicIp = request.getParameter( "PublicIp" ); + if (publicIp == null) { + response.sendError(530, "Missing PublicIp parameter"); + return; + } + + EC2ReleaseAddress ec2Request = new EC2ReleaseAddress(); + if (ec2Request != null) { + ec2Request.setPublicIp(publicIp); + } + + ReleaseAddressResponse EC2Response = EC2SoapServiceImpl.toReleaseAddressResponse( engine.releaseAddress( ec2Request )); + + serializeResponse(response, EC2Response); } private void associateAddress( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { + throws ADBException, XMLStreamException, IOException { EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); String publicIp = request.getParameter( "PublicIp" ); @@ -1324,20 +1324,20 @@ public class EC2RestServlet extends HttpServlet { response.sendError(530, "Missing InstanceId parameter" ); return; } - + EC2AssociateAddress ec2Request = new EC2AssociateAddress(); if (ec2Request != null) { - ec2Request.setInstanceId(instanceId); - ec2Request.setPublicIp(publicIp); + ec2Request.setInstanceId(instanceId); + ec2Request.setPublicIp(publicIp); } AssociateAddressResponse ec2Response = EC2SoapServiceImpl.toAssociateAddressResponse( engine.associateAddress( ec2Request )); - + serializeResponse(response, ec2Response); } private void disassociateAddress( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { + throws ADBException, XMLStreamException, IOException { EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); String publicIp = request.getParameter( "PublicIp" ); @@ -1345,145 +1345,145 @@ public class EC2RestServlet extends HttpServlet { response.sendError(530, "Missing PublicIp parameter" ); return; } - + EC2DisassociateAddress ec2Request = new EC2DisassociateAddress(); if (ec2Request != null) { - ec2Request.setPublicIp(publicIp); + ec2Request.setPublicIp(publicIp); } - + DisassociateAddressResponse ec2Response = EC2SoapServiceImpl.toDisassociateAddressResponse( engine.disassociateAddress( ec2Request ) ); - + serializeResponse(response, ec2Response); } - + private void describeSecurityGroups( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException - { - EC2DescribeSecurityGroups EC2request = new EC2DescribeSecurityGroups(); - - // -> load in all the "GroupName.n" parameters if any - Enumeration names = request.getParameterNames(); - while( names.hasMoreElements()) { - String key = (String)names.nextElement(); - if (key.startsWith("GroupName")) { - String[] value = request.getParameterValues( key ); - if (null != value && 0 < value.length) EC2request.addGroupName( value[0] ); - } - } - + throws ADBException, XMLStreamException, IOException + { + EC2DescribeSecurityGroups EC2request = new EC2DescribeSecurityGroups(); + + // -> load in all the "GroupName.n" parameters if any + Enumeration names = request.getParameterNames(); + while( names.hasMoreElements()) { + String key = (String)names.nextElement(); + if (key.startsWith("GroupName")) { + String[] value = request.getParameterValues( key ); + if (null != value && 0 < value.length) EC2request.addGroupName( value[0] ); + } + } + // -> are there any filters with this request? EC2Filter[] filterSet = extractFilters( request ); if (null != filterSet) { - EC2GroupFilterSet gfs = new EC2GroupFilterSet(); - for (EC2Filter filter : filterSet) gfs.addFilter( filter ); - EC2request.setFilterSet( gfs ); + EC2GroupFilterSet gfs = new EC2GroupFilterSet(); + for (EC2Filter filter : filterSet) gfs.addFilter( filter ); + EC2request.setFilterSet( gfs ); } - - // -> execute the request - EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); - - DescribeSecurityGroupsResponse EC2response = EC2SoapServiceImpl.toDescribeSecurityGroupsResponse( engine.describeSecurityGroups( EC2request )); - serializeResponse(response, EC2response); - } - - + + // -> execute the request + EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); + + DescribeSecurityGroupsResponse EC2response = EC2SoapServiceImpl.toDescribeSecurityGroupsResponse( engine.describeSecurityGroups( EC2request )); + serializeResponse(response, EC2response); + } + + private void describeInstanceAttribute( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException { - EC2DescribeInstances EC2request = new EC2DescribeInstances(); - String instanceType = null; - - // -> we are only handling queries about the "Attribute=instanceType" - Enumeration names = request.getParameterNames(); - while( names.hasMoreElements()) { - String key = (String)names.nextElement(); - if (key.startsWith("Attribute")) { - String[] value = request.getParameterValues( key ); - if (null != value && 0 < value.length && value[0].equalsIgnoreCase( "instanceType" )) { - instanceType = value[0]; - break; - } - } - } - if ( null != instanceType ) { - String[] value = request.getParameterValues( "InstanceId" ); - EC2request.addInstanceId( value[0] ); - } - else { - response.sendError(501, "Unsupported - only instanceType supported" ); - return; - } - - // -> execute the request - DescribeInstanceAttributeResponse EC2response = EC2SoapServiceImpl.toDescribeInstanceAttributeResponse( ServiceProvider.getInstance().getEC2Engine().describeInstances(EC2request)); - serializeResponse(response, EC2response); + throws ADBException, XMLStreamException, IOException { + EC2DescribeInstances EC2request = new EC2DescribeInstances(); + String instanceType = null; + + // -> we are only handling queries about the "Attribute=instanceType" + Enumeration names = request.getParameterNames(); + while( names.hasMoreElements()) { + String key = (String)names.nextElement(); + if (key.startsWith("Attribute")) { + String[] value = request.getParameterValues( key ); + if (null != value && 0 < value.length && value[0].equalsIgnoreCase( "instanceType" )) { + instanceType = value[0]; + break; + } + } + } + if ( null != instanceType ) { + String[] value = request.getParameterValues( "InstanceId" ); + EC2request.addInstanceId( value[0] ); + } + else { + response.sendError(501, "Unsupported - only instanceType supported" ); + return; + } + + // -> execute the request + DescribeInstanceAttributeResponse EC2response = EC2SoapServiceImpl.toDescribeInstanceAttributeResponse( ServiceProvider.getInstance().getEC2Engine().describeInstances(EC2request)); + serializeResponse(response, EC2response); } - + private void describeSnapshots( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException - { - EC2DescribeSnapshots EC2request = new EC2DescribeSnapshots(); - - // -> load in all the "SnapshotId.n" parameters if any, and ignore any other parameters - Enumeration names = request.getParameterNames(); - while( names.hasMoreElements()) - { - String key = (String)names.nextElement(); - if (key.startsWith("SnapshotId")) { - String[] value = request.getParameterValues( key ); - if (null != value && 0 < value.length) EC2request.addSnapshotId( value[0] ); - } - } - + throws ADBException, XMLStreamException, IOException + { + EC2DescribeSnapshots EC2request = new EC2DescribeSnapshots(); + + // -> load in all the "SnapshotId.n" parameters if any, and ignore any other parameters + Enumeration names = request.getParameterNames(); + while( names.hasMoreElements()) + { + String key = (String)names.nextElement(); + if (key.startsWith("SnapshotId")) { + String[] value = request.getParameterValues( key ); + if (null != value && 0 < value.length) EC2request.addSnapshotId( value[0] ); + } + } + // -> are there any filters with this request? EC2Filter[] filterSet = extractFilters( request ); if (null != filterSet) { - EC2SnapshotFilterSet sfs = new EC2SnapshotFilterSet(); - for( int i=0; i < filterSet.length; i++ ) sfs.addFilter( filterSet[i] ); - EC2request.setFilterSet( sfs ); + EC2SnapshotFilterSet sfs = new EC2SnapshotFilterSet(); + for( int i=0; i < filterSet.length; i++ ) sfs.addFilter( filterSet[i] ); + EC2request.setFilterSet( sfs ); } - // -> execute the request - EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); - DescribeSnapshotsResponse EC2response = EC2SoapServiceImpl.toDescribeSnapshotsResponse( engine.handleRequest( EC2request )); - serializeResponse(response, EC2response); - } + // -> execute the request + EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); + DescribeSnapshotsResponse EC2response = EC2SoapServiceImpl.toDescribeSnapshotsResponse( engine.handleRequest( EC2request )); + serializeResponse(response, EC2response); + } + - private void describeVolumes( HttpServletRequest request, HttpServletResponse response ) - throws ADBException, XMLStreamException, IOException - { + throws ADBException, XMLStreamException, IOException + { EC2DescribeVolumes EC2request = new EC2DescribeVolumes(); // -> load in all the "VolumeId.n" parameters if any Enumeration names = request.getParameterNames(); while( names.hasMoreElements()) { - String key = (String)names.nextElement(); - if (key.startsWith("VolumeId")) - { - String[] value = request.getParameterValues( key ); - if (null != value && 0 < value.length) EC2request.addVolumeId( value[0] ); - } + String key = (String)names.nextElement(); + if (key.startsWith("VolumeId")) + { + String[] value = request.getParameterValues( key ); + if (null != value && 0 < value.length) EC2request.addVolumeId( value[0] ); + } } - + // -> are there any filters with this request? EC2Filter[] filterSet = extractFilters( request ); if (null != filterSet) { - EC2VolumeFilterSet vfs = new EC2VolumeFilterSet(); - for( int i=0; i < filterSet.length; i++ ) vfs.addFilter( filterSet[i] ); - EC2request.setFilterSet( vfs ); + EC2VolumeFilterSet vfs = new EC2VolumeFilterSet(); + for( int i=0; i < filterSet.length; i++ ) vfs.addFilter( filterSet[i] ); + EC2request.setFilterSet( vfs ); } - + // -> execute the request DescribeVolumesResponse EC2response = EC2SoapServiceImpl.toDescribeVolumesResponse( ServiceProvider.getInstance().getEC2Engine().handleRequest( EC2request )); serializeResponse(response, EC2response); - } - - + } + + /** * Example of how the filters are defined in a REST request: * https:///?Action=DescribeVolumes @@ -1497,148 +1497,148 @@ public class EC2RestServlet extends HttpServlet { */ private EC2Filter[] extractFilters( HttpServletRequest request ) { - String filterName = null; - String value = null; - EC2Filter nextFilter = null; - boolean timeFilter = false; - int filterCount = 1; - int valueCount = 1; - - List filterSet = new ArrayList(); - - do - { filterName = request.getParameter( "Filter." + filterCount + ".Name" ); - if (null != filterName) - { - nextFilter = new EC2Filter(); - nextFilter.setName( filterName ); - timeFilter = (filterName.equalsIgnoreCase( "attachment.attach-time" ) || filterName.equalsIgnoreCase( "create-time" )); - valueCount = 1; - do - { - value = request.getParameter( "Filter." + filterCount + ".Value." + valueCount ); - if (null != value) - { - // -> time values are not encoded as regexes - if ( timeFilter ) - nextFilter.addValue( value ); - else nextFilter.addValueEncoded( value ); - - valueCount++; - } - } - while( null != value ); - - filterSet.add( nextFilter ); - filterCount++; - } - } - while( null != filterName ); - - if ( 1 == filterCount ) - return null; - else return filterSet.toArray(new EC2Filter[0]); + String filterName = null; + String value = null; + EC2Filter nextFilter = null; + boolean timeFilter = false; + int filterCount = 1; + int valueCount = 1; + + List filterSet = new ArrayList(); + + do + { filterName = request.getParameter( "Filter." + filterCount + ".Name" ); + if (null != filterName) + { + nextFilter = new EC2Filter(); + nextFilter.setName( filterName ); + timeFilter = (filterName.equalsIgnoreCase( "attachment.attach-time" ) || filterName.equalsIgnoreCase( "create-time" )); + valueCount = 1; + do + { + value = request.getParameter( "Filter." + filterCount + ".Value." + valueCount ); + if (null != value) + { + // -> time values are not encoded as regexes + if ( timeFilter ) + nextFilter.addValue( value ); + else nextFilter.addValueEncoded( value ); + + valueCount++; + } + } + while( null != value ); + + filterSet.add( nextFilter ); + filterCount++; + } + } + while( null != filterName ); + + if ( 1 == filterCount ) + return null; + else return filterSet.toArray(new EC2Filter[0]); } - + private void describeKeyPairs(HttpServletRequest request, HttpServletResponse response) - throws ADBException, XMLStreamException, IOException { - EC2DescribeKeyPairs ec2Request = new EC2DescribeKeyPairs(); - - + throws ADBException, XMLStreamException, IOException { + EC2DescribeKeyPairs ec2Request = new EC2DescribeKeyPairs(); + + String[] keyNames = request.getParameterValues( "KeyName" ); if (keyNames != null) { - for (String keyName : keyNames) { - ec2Request.addKeyName(keyName); - } + for (String keyName : keyNames) { + ec2Request.addKeyName(keyName); + } } - EC2Filter[] filterSet = extractFilters( request ); + EC2Filter[] filterSet = extractFilters( request ); if (null != filterSet){ - EC2KeyPairFilterSet vfs = new EC2KeyPairFilterSet(); - for (EC2Filter filter : filterSet) { - vfs.addFilter(filter); - } - ec2Request.setKeyFilterSet(vfs); + EC2KeyPairFilterSet vfs = new EC2KeyPairFilterSet(); + for (EC2Filter filter : filterSet) { + vfs.addFilter(filter); + } + ec2Request.setKeyFilterSet(vfs); } - DescribeKeyPairsResponse EC2Response = EC2SoapServiceImpl.toDescribeKeyPairs( - ServiceProvider.getInstance().getEC2Engine().describeKeyPairs( ec2Request )); - serializeResponse(response, EC2Response); + DescribeKeyPairsResponse EC2Response = EC2SoapServiceImpl.toDescribeKeyPairs( + ServiceProvider.getInstance().getEC2Engine().describeKeyPairs( ec2Request )); + serializeResponse(response, EC2Response); } private void importKeyPair(HttpServletRequest request, HttpServletResponse response) - throws ADBException, XMLStreamException, IOException { - - String keyName = request.getParameter("KeyName"); - String publicKeyMaterial = request.getParameter("PublicKeyMaterial"); - if (keyName==null && publicKeyMaterial==null) { - response.sendError(530, "Missing parameter"); - return; - } + throws ADBException, XMLStreamException, IOException { - if (!publicKeyMaterial.contains(" ")) + String keyName = request.getParameter("KeyName"); + String publicKeyMaterial = request.getParameter("PublicKeyMaterial"); + if (keyName==null && publicKeyMaterial==null) { + response.sendError(530, "Missing parameter"); + return; + } + + if (!publicKeyMaterial.contains(" ")) publicKeyMaterial = new String(Base64.decodeBase64(publicKeyMaterial.getBytes())); - - - EC2ImportKeyPair ec2Request = new EC2ImportKeyPair(); - if (ec2Request != null) { - ec2Request.setKeyName(request.getParameter("KeyName")); - ec2Request.setPublicKeyMaterial(request.getParameter("PublicKeyMaterial")); - } - - ImportKeyPairResponse EC2Response = EC2SoapServiceImpl.toImportKeyPair( - ServiceProvider.getInstance().getEC2Engine().importKeyPair( ec2Request )); - serializeResponse(response, EC2Response); + + + EC2ImportKeyPair ec2Request = new EC2ImportKeyPair(); + if (ec2Request != null) { + ec2Request.setKeyName(request.getParameter("KeyName")); + ec2Request.setPublicKeyMaterial(request.getParameter("PublicKeyMaterial")); + } + + ImportKeyPairResponse EC2Response = EC2SoapServiceImpl.toImportKeyPair( + ServiceProvider.getInstance().getEC2Engine().importKeyPair( ec2Request )); + serializeResponse(response, EC2Response); } private void createKeyPair(HttpServletRequest request, HttpServletResponse response) - throws ADBException, XMLStreamException, IOException { - String keyName = request.getParameter("KeyName"); - if (keyName==null) { - response.sendError(530, "Missing KeyName parameter"); - return; - } - - EC2CreateKeyPair ec2Request = new EC2CreateKeyPair(); - if (ec2Request != null) { - ec2Request.setKeyName(keyName); - } - - CreateKeyPairResponse EC2Response = EC2SoapServiceImpl.toCreateKeyPair( - ServiceProvider.getInstance().getEC2Engine().createKeyPair(ec2Request)); - serializeResponse(response, EC2Response); + throws ADBException, XMLStreamException, IOException { + String keyName = request.getParameter("KeyName"); + if (keyName==null) { + response.sendError(530, "Missing KeyName parameter"); + return; + } + + EC2CreateKeyPair ec2Request = new EC2CreateKeyPair(); + if (ec2Request != null) { + ec2Request.setKeyName(keyName); + } + + CreateKeyPairResponse EC2Response = EC2SoapServiceImpl.toCreateKeyPair( + ServiceProvider.getInstance().getEC2Engine().createKeyPair(ec2Request)); + serializeResponse(response, EC2Response); } private void deleteKeyPair(HttpServletRequest request, HttpServletResponse response) - throws ADBException, XMLStreamException, IOException { - String keyName = request.getParameter("KeyName"); - if (keyName==null) { - response.sendError(530, "Missing KeyName parameter"); - return; - } - - EC2DeleteKeyPair ec2Request = new EC2DeleteKeyPair(); - ec2Request.setKeyName(keyName); - - DeleteKeyPairResponse EC2Response = EC2SoapServiceImpl.toDeleteKeyPair( - ServiceProvider.getInstance().getEC2Engine().deleteKeyPair(ec2Request)); - serializeResponse(response, EC2Response); + throws ADBException, XMLStreamException, IOException { + String keyName = request.getParameter("KeyName"); + if (keyName==null) { + response.sendError(530, "Missing KeyName parameter"); + return; + } + + EC2DeleteKeyPair ec2Request = new EC2DeleteKeyPair(); + ec2Request.setKeyName(keyName); + + DeleteKeyPairResponse EC2Response = EC2SoapServiceImpl.toDeleteKeyPair( + ServiceProvider.getInstance().getEC2Engine().deleteKeyPair(ec2Request)); + serializeResponse(response, EC2Response); } - + private void getPasswordData(HttpServletRequest request, HttpServletResponse response) - throws ADBException, XMLStreamException, IOException { - String instanceId = request.getParameter("InstanceId"); - if (instanceId==null) { - response.sendError(530, "Missing InstanceId parameter"); - return; - } - - GetPasswordDataResponse EC2Response = EC2SoapServiceImpl.toGetPasswordData( - ServiceProvider.getInstance().getEC2Engine().getPasswordData(instanceId)); - serializeResponse(response, EC2Response); + throws ADBException, XMLStreamException, IOException { + String instanceId = request.getParameter("InstanceId"); + if (instanceId==null) { + response.sendError(530, "Missing InstanceId parameter"); + return; + } + + GetPasswordDataResponse EC2Response = EC2SoapServiceImpl.toGetPasswordData( + ServiceProvider.getInstance().getEC2Engine().getPasswordData(instanceId)); + serializeResponse(response, EC2Response); } - + /** * This function implements the EC2 REST authentication algorithm. It uses the given * "AWSAccessKeyId" parameter to look up the Cloud.com account holder's secret key which is @@ -1646,107 +1646,107 @@ public class EC2RestServlet extends HttpServlet { * parameter to see if the signature has expired and if so the request fails. */ private boolean authenticateRequest( HttpServletRequest request, HttpServletResponse response ) - throws SignatureException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException, ParseException - { - String cloudSecretKey = null; - String cloudAccessKey = null; - String signature = null; - String sigMethod = null; + throws SignatureException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException, ParseException + { + String cloudSecretKey = null; + String cloudAccessKey = null; + String signature = null; + String sigMethod = null; - // [A] Basic parameters required for an authenticated rest request - // -> note that the Servlet engine will un-URL encode all parameters we extract via "getParameterValues()" calls + // [A] Basic parameters required for an authenticated rest request + // -> note that the Servlet engine will un-URL encode all parameters we extract via "getParameterValues()" calls String[] awsAccess = request.getParameterValues( "AWSAccessKeyId" ); - if ( null != awsAccess && 0 < awsAccess.length ) - cloudAccessKey = awsAccess[0]; - else { response.sendError(530, "Missing AWSAccessKeyId parameter" ); return false; } + if ( null != awsAccess && 0 < awsAccess.length ) + cloudAccessKey = awsAccess[0]; + else { response.sendError(530, "Missing AWSAccessKeyId parameter" ); return false; } String[] clientSig = request.getParameterValues( "Signature" ); - if ( null != clientSig && 0 < clientSig.length ) - signature = clientSig[0]; - else { response.sendError(530, "Missing Signature parameter" ); return false; } + if ( null != clientSig && 0 < clientSig.length ) + signature = clientSig[0]; + else { response.sendError(530, "Missing Signature parameter" ); return false; } String[] method = request.getParameterValues( "SignatureMethod" ); - if ( null != method && 0 < method.length ) - { - sigMethod = method[0]; - if (!sigMethod.equals( "HmacSHA256" ) && !sigMethod.equals( "HmacSHA1" )) { - response.sendError(531, "Unsupported SignatureMethod value: " + sigMethod + " expecting: HmacSHA256 or HmacSHA1" ); - return false; - } - } - else { response.sendError(530, "Missing SignatureMethod parameter" ); return false; } + if ( null != method && 0 < method.length ) + { + sigMethod = method[0]; + if (!sigMethod.equals( "HmacSHA256" ) && !sigMethod.equals( "HmacSHA1" )) { + response.sendError(531, "Unsupported SignatureMethod value: " + sigMethod + " expecting: HmacSHA256 or HmacSHA1" ); + return false; + } + } + else { response.sendError(530, "Missing SignatureMethod parameter" ); return false; } String[] version = request.getParameterValues( "Version" ); - if ( null != version && 0 < version.length ) - { - if (!version[0].equals( wsdlVersion )) { - response.sendError(531, "Unsupported Version value: " + version[0] + " expecting: " + wsdlVersion ); - return false; - } - } - else { response.sendError(530, "Missing Version parameter" ); return false; } + if ( null != version && 0 < version.length ) + { + if (!version[0].equals( wsdlVersion )) { + response.sendError(531, "Unsupported Version value: " + version[0] + " expecting: " + wsdlVersion ); + return false; + } + } + else { response.sendError(530, "Missing Version parameter" ); return false; } String[] sigVersion = request.getParameterValues( "SignatureVersion" ); - if ( null != sigVersion && 0 < sigVersion.length ) - { - if (!sigVersion[0].equals( "2" )) { - response.sendError(531, "Unsupported SignatureVersion value: " + sigVersion[0] + " expecting: 2" ); - return false; - } - } - else { response.sendError(530, "Missing SignatureVersion parameter" ); return false; } + if ( null != sigVersion && 0 < sigVersion.length ) + { + if (!sigVersion[0].equals( "2" )) { + response.sendError(531, "Unsupported SignatureVersion value: " + sigVersion[0] + " expecting: 2" ); + return false; + } + } + else { response.sendError(530, "Missing SignatureVersion parameter" ); return false; } - // -> can have only one but not both { Expires | Timestamp } headers + // -> can have only one but not both { Expires | Timestamp } headers String[] expires = request.getParameterValues( "Expires" ); - if ( null != expires && 0 < expires.length ) - { - // -> contains the date and time at which the signature included in the request EXPIRES - if (hasSignatureExpired( expires[0] )) { - response.sendError(531, "Expires parameter indicates signature has expired: " + expires[0] ); - return false; - } - } - else - { // -> contains the date and time at which the request is SIGNED - String[] time = request.getParameterValues( "Timestamp" ); - if ( null == time || 0 == time.length ) { - response.sendError(530, "Missing Timestamp and Expires parameter, one is required" ); - return false; - } - } - - // [B] Use the cloudAccessKey to get the users secret key in the db - UserCredentialsVO cloudKeys = ucDao.getByAccessKey( cloudAccessKey ); + if ( null != expires && 0 < expires.length ) + { + // -> contains the date and time at which the signature included in the request EXPIRES + if (hasSignatureExpired( expires[0] )) { + response.sendError(531, "Expires parameter indicates signature has expired: " + expires[0] ); + return false; + } + } + else + { // -> contains the date and time at which the request is SIGNED + String[] time = request.getParameterValues( "Timestamp" ); + if ( null == time || 0 == time.length ) { + response.sendError(530, "Missing Timestamp and Expires parameter, one is required" ); + return false; + } + } - if ( null == cloudKeys ) - { - logger.debug( cloudAccessKey + " is not defined in the EC2 service - call SetUserKeys" ); - response.sendError(404, cloudAccessKey + " is not defined in the EC2 service - call SetUserKeys" ); - return false; - } - else cloudSecretKey = cloudKeys.getSecretKey(); + // [B] Use the cloudAccessKey to get the users secret key in the db + UserCredentialsVO cloudKeys = ucDao.getByAccessKey( cloudAccessKey ); - - // [C] Verify the signature - // -> getting the query-string in this way maintains its URL encoding - EC2RestAuth restAuth = new EC2RestAuth(); - restAuth.setHostHeader( request.getHeader( "Host" )); - String requestUri = request.getRequestURI(); - - // If forwarded from another basepath: - String forwardedPath = (String) request.getAttribute("javax.servlet.forward.request_uri"); - if(forwardedPath!=null){ - requestUri=forwardedPath; - } - restAuth.setHTTPRequestURI( requestUri); + if ( null == cloudKeys ) + { + logger.debug( cloudAccessKey + " is not defined in the EC2 service - call SetUserKeys" ); + response.sendError(404, cloudAccessKey + " is not defined in the EC2 service - call SetUserKeys" ); + return false; + } + else cloudSecretKey = cloudKeys.getSecretKey(); - String queryString = request.getQueryString(); - // getQueryString returns null (does it ever NOT return null for these), - // we need to construct queryString to avoid changing the auth code... - if (queryString == null) { - // construct our idea of a queryString with parameters! - Enumeration params = request.getParameterNames(); - if (params != null) { + + // [C] Verify the signature + // -> getting the query-string in this way maintains its URL encoding + EC2RestAuth restAuth = new EC2RestAuth(); + restAuth.setHostHeader( request.getHeader( "Host" )); + String requestUri = request.getRequestURI(); + + // If forwarded from another basepath: + String forwardedPath = (String) request.getAttribute("javax.servlet.forward.request_uri"); + if(forwardedPath!=null){ + requestUri=forwardedPath; + } + restAuth.setHTTPRequestURI( requestUri); + + String queryString = request.getQueryString(); + // getQueryString returns null (does it ever NOT return null for these), + // we need to construct queryString to avoid changing the auth code... + if (queryString == null) { + // construct our idea of a queryString with parameters! + Enumeration params = request.getParameterNames(); + if (params != null) { while(params.hasMoreElements()) { String paramName = (String) params.nextElement(); // exclude the signature string obviously. ;) @@ -1756,16 +1756,16 @@ public class EC2RestServlet extends HttpServlet { else queryString = queryString + "&" + paramName + "=" + URLEncoder.encode(request.getParameter(paramName), "UTF-8"); } - } - } - restAuth.setQueryString(queryString); - - if ( restAuth.verifySignature( request.getMethod(), cloudSecretKey, signature, sigMethod )) { - UserContext.current().initContext( cloudAccessKey, cloudSecretKey, cloudAccessKey, "REST request", null ); - return true; - } - else throw new PermissionDeniedException("Invalid signature"); - } + } + } + restAuth.setQueryString(queryString); + + if ( restAuth.verifySignature( request.getMethod(), cloudSecretKey, signature, sigMethod )) { + UserContext.current().initContext( cloudAccessKey, cloudSecretKey, cloudAccessKey, "REST request", null ); + return true; + } + else throw new PermissionDeniedException("Invalid signature"); + } /** * We check this to reduce replay attacks. @@ -1777,94 +1777,94 @@ public class EC2RestServlet extends HttpServlet { private boolean hasSignatureExpired( String timeStamp ) { Calendar cal = EC2RestAuth.parseDateString( timeStamp ); if (null == cal) return false; - + Date expiredTime = cal.getTime(); - Date today = new Date(); // -> gets set to time of creation + Date today = new Date(); // -> gets set to time of creation if ( 0 >= expiredTime.compareTo( today )) { - logger.debug( "timestamp given: [" + timeStamp + "], now: [" + today.toString() + "]" ); - return true; + logger.debug( "timestamp given: [" + timeStamp + "], now: [" + today.toString() + "]" ); + return true; } else return false; } - + private static void endResponse(HttpServletResponse response, String content) { - try { + try { byte[] data = content.getBytes(); response.setContentLength(data.length); OutputStream os = response.getOutputStream(); os.write(data); os.close(); - - } catch(Throwable e) { - logger.error("Unexpected exception " + e.getMessage(), e); - } + + } catch(Throwable e) { + logger.error("Unexpected exception " + e.getMessage(), e); + } } private void logRequest(HttpServletRequest request) { - if(logger.isInfoEnabled()) { - logger.info("EC2 Request method: " + request.getMethod()); - logger.info("Request contextPath: " + request.getContextPath()); - logger.info("Request pathInfo: " + request.getPathInfo()); - logger.info("Request pathTranslated: " + request.getPathTranslated()); - logger.info("Request queryString: " + request.getQueryString()); - logger.info("Request requestURI: " + request.getRequestURI()); - logger.info("Request requestURL: " + request.getRequestURL()); - logger.info("Request servletPath: " + request.getServletPath()); - Enumeration headers = request.getHeaderNames(); - if(headers != null) { - while(headers.hasMoreElements()) { - Object headerName = headers.nextElement(); - logger.info("Request header " + headerName + ":" + request.getHeader((String)headerName)); - } - } - - Enumeration params = request.getParameterNames(); - if(params != null) { - while(params.hasMoreElements()) { - Object paramName = params.nextElement(); - logger.info("Request parameter " + paramName + ":" + - request.getParameter((String)paramName)); - } - } - } + if(logger.isInfoEnabled()) { + logger.info("EC2 Request method: " + request.getMethod()); + logger.info("Request contextPath: " + request.getContextPath()); + logger.info("Request pathInfo: " + request.getPathInfo()); + logger.info("Request pathTranslated: " + request.getPathTranslated()); + logger.info("Request queryString: " + request.getQueryString()); + logger.info("Request requestURI: " + request.getRequestURI()); + logger.info("Request requestURL: " + request.getRequestURL()); + logger.info("Request servletPath: " + request.getServletPath()); + Enumeration headers = request.getHeaderNames(); + if(headers != null) { + while(headers.hasMoreElements()) { + Object headerName = headers.nextElement(); + logger.info("Request header " + headerName + ":" + request.getHeader((String)headerName)); + } + } + + Enumeration params = request.getParameterNames(); + if(params != null) { + while(params.hasMoreElements()) { + Object paramName = params.nextElement(); + logger.info("Request parameter " + paramName + ":" + + request.getParameter((String)paramName)); + } + } + } } - + /** - * Send out an error response according to Amazon convention. + * Send out an error response according to Amazon convention. */ private void faultResponse(HttpServletResponse response, String errorCode, String errorMessage) { try { - OutputStreamWriter out = new OutputStreamWriter(response.getOutputStream()); - response.setContentType("text/xml; charset=UTF-8"); - out.write(""); - out.write(""); - out.write(errorCode); - out.write(""); - out.write(errorMessage); - out.write(""); - out.write(UUID.randomUUID().toString()); - out.write(""); - out.flush(); - out.close(); - } catch (IOException e) { - logger.error("Unexpected exception " + e.getMessage(), e); - } + OutputStreamWriter out = new OutputStreamWriter(response.getOutputStream()); + response.setContentType("text/xml; charset=UTF-8"); + out.write(""); + out.write(""); + out.write(errorCode); + out.write(""); + out.write(errorMessage); + out.write(""); + out.write(UUID.randomUUID().toString()); + out.write(""); + out.flush(); + out.close(); + } catch (IOException e) { + logger.error("Unexpected exception " + e.getMessage(), e); + } } - + /** - * Serialize Axis beans to XML output. + * Serialize Axis beans to XML output. */ private void serializeResponse(HttpServletResponse response, ADBBean EC2Response) - throws ADBException, XMLStreamException, IOException { - OutputStream os = response.getOutputStream(); - response.setStatus(200); - response.setContentType("text/xml; charset=UTF-8"); - XMLStreamWriter xmlWriter = xmlOutFactory.createXMLStreamWriter( os ); - MTOMAwareXMLSerializer MTOMWriter = new MTOMAwareXMLSerializer( xmlWriter ); - MTOMWriter.setDefaultNamespace("http://ec2.amazonaws.com/doc/" + wsdlVersion + "/"); - EC2Response.serialize( null, factory, MTOMWriter ); - xmlWriter.flush(); - xmlWriter.close(); - os.close(); + throws ADBException, XMLStreamException, IOException { + OutputStream os = response.getOutputStream(); + response.setStatus(200); + response.setContentType("text/xml; charset=UTF-8"); + XMLStreamWriter xmlWriter = xmlOutFactory.createXMLStreamWriter( os ); + MTOMAwareXMLSerializer MTOMWriter = new MTOMAwareXMLSerializer( xmlWriter ); + MTOMWriter.setDefaultNamespace("http://ec2.amazonaws.com/doc/" + wsdlVersion + "/"); + EC2Response.serialize( null, factory, MTOMWriter ); + xmlWriter.flush(); + xmlWriter.close(); + os.close(); } } diff --git a/awsapi/src/com/cloud/bridge/service/S3RestServlet.java b/awsapi/src/com/cloud/bridge/service/S3RestServlet.java index c1458a7b4af..c824fcadd5b 100644 --- a/awsapi/src/com/cloud/bridge/service/S3RestServlet.java +++ b/awsapi/src/com/cloud/bridge/service/S3RestServlet.java @@ -18,21 +18,22 @@ package com.cloud.bridge.service; import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.OutputStream; import java.io.InputStream; +import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.security.SignatureException; import java.sql.SQLException; import java.util.Enumeration; +import javax.inject.Inject; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.xml.bind.DatatypeConverter; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.bind.*; import org.apache.axis2.AxisFault; import org.apache.log4j.Logger; @@ -45,10 +46,7 @@ import com.cloud.bridge.io.MultiPartDimeInputStream; import com.cloud.bridge.model.SAcl; import com.cloud.bridge.model.UserCredentialsVO; import com.cloud.bridge.persist.dao.CloudStackConfigurationDao; -import com.cloud.bridge.persist.dao.CloudStackConfigurationDaoImpl; import com.cloud.bridge.persist.dao.UserCredentialsDao; - -import com.cloud.bridge.persist.dao.UserCredentialsDaoImpl; import com.cloud.bridge.service.controller.s3.S3BucketAction; import com.cloud.bridge.service.controller.s3.S3ObjectAction; import com.cloud.bridge.service.controller.s3.ServiceProvider; @@ -66,151 +64,155 @@ import com.cloud.bridge.util.ConfigurationHelper; import com.cloud.bridge.util.HeaderParam; import com.cloud.bridge.util.RestAuth; import com.cloud.bridge.util.S3SoapAuth; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; - -import net.sf.ehcache.Cache; public class S3RestServlet extends HttpServlet { - private static final long serialVersionUID = -6168996266762804877L; - public static final String ENABLE_S3_API="enable.s3.api"; - private static boolean isS3APIEnabled = false; + private static final long serialVersionUID = -6168996266762804877L; + public static final String ENABLE_S3_API="enable.s3.api"; + private static boolean isS3APIEnabled = false; - public static final Logger logger = Logger.getLogger(S3RestServlet.class); - protected final CloudStackConfigurationDao csDao = ComponentLocator.inject(CloudStackConfigurationDaoImpl.class); - protected final UserCredentialsDao ucDao = ComponentLocator.inject(UserCredentialsDaoImpl.class); - - protected void doGet(HttpServletRequest req, HttpServletResponse resp) { - processRequest( req, resp, "GET" ); - } - + public static final Logger logger = Logger.getLogger(S3RestServlet.class); + @Inject CloudStackConfigurationDao csDao; + @Inject UserCredentialsDao ucDao; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + processRequest( req, resp, "GET" ); + } + + @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) { - // -> DIME requests are authenticated via the SOAP auth mechanism - String type = req.getHeader( "Content-Type" ); - if ( null != type && type.equalsIgnoreCase( "application/dime" )) - processDimeRequest(req, resp); - else processRequest( req, resp, "POST" ); + // -> DIME requests are authenticated via the SOAP auth mechanism + String type = req.getHeader( "Content-Type" ); + if ( null != type && type.equalsIgnoreCase( "application/dime" )) + processDimeRequest(req, resp); + else processRequest( req, resp, "POST" ); } - + + @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) { processRequest( req, resp, "PUT" ); } - + + @Override protected void doHead(HttpServletRequest req, HttpServletResponse resp) { processRequest( req, resp, "HEAD" ); } - + + @Override protected void doOptions(HttpServletRequest req, HttpServletResponse resp) { processRequest( req, resp, "OPTIONS" ); } - + + @Override protected void doDelete( HttpServletRequest req, HttpServletResponse resp ) { processRequest( req, resp, "DELETE" ); } - + + @Override public void init( ServletConfig config ) throws ServletException { - try{ - ConfigurationHelper.preConfigureConfigPathFromServletContext(config.getServletContext()); - // check if API is enabled - String value = csDao.getConfigValue(ENABLE_S3_API); - if(value != null) { - isS3APIEnabled = Boolean.valueOf(value); - } - logger.info("S3Engine :: Configuration value is : " + value); - - }catch(Exception e){ - throw new ServletException("Error initializing awsapi: " + e.getMessage()); + try{ + ConfigurationHelper.preConfigureConfigPathFromServletContext(config.getServletContext()); + // check if API is enabled + String value = csDao.getConfigValue(ENABLE_S3_API); + if(value != null) { + isS3APIEnabled = Boolean.valueOf(value); + } + logger.info("S3Engine :: Configuration value is : " + value); + + }catch(Exception e){ + throw new ServletException("Error initializing awsapi: " + e.getMessage()); } - - } - - - - /** - * POST requests do not get authenticated on entry. The associated - * access key and signature headers are embedded in the message not encoded - * as HTTP headers. - */ + + } + + + + /** + * POST requests do not get authenticated on entry. The associated + * access key and signature headers are embedded in the message not encoded + * as HTTP headers. + */ private void processRequest( HttpServletRequest request, HttpServletResponse response, String method ) { Transaction txn = Transaction.open("cloudbridge", Transaction.AWSAPI_DB, true); try { - logRequest(request); - - // Our extensions to the S3 REST API for simple management actions - // are conveyed with Request parameter "Action". - // The present extensions are either to set up the user credentials - // (see the cloud-bridge-register script for more detail) or - // to report our version of this capability. - // -> unauthenticated calls, should still be done over HTTPS - String cloudAction = request.getParameter( "Action" ); - - if(!isS3APIEnabled){ - throw new RuntimeException("Amazon S3 API is disabled."); - } - - + logRequest(request); + + // Our extensions to the S3 REST API for simple management actions + // are conveyed with Request parameter "Action". + // The present extensions are either to set up the user credentials + // (see the cloud-bridge-register script for more detail) or + // to report our version of this capability. + // -> unauthenticated calls, should still be done over HTTPS + String cloudAction = request.getParameter( "Action" ); + + if(!isS3APIEnabled){ + throw new RuntimeException("Amazon S3 API is disabled."); + } + + if (null != cloudAction) { - if (cloudAction.equalsIgnoreCase( "SetUserKeys" )) { - setUserKeys(request, response); - return; - } - - if (cloudAction.equalsIgnoreCase( "SetCertificate" )) - // At present a noop - return; + if (cloudAction.equalsIgnoreCase( "SetUserKeys" )) { + setUserKeys(request, response); + return; + } + + if (cloudAction.equalsIgnoreCase( "SetCertificate" )) + // At present a noop + return; + + if (cloudAction.equalsIgnoreCase( "CloudS3Version" )) { + cloudS3Version(request, response); + return; + } + } - if (cloudAction.equalsIgnoreCase( "CloudS3Version" )) { - cloudS3Version(request, response); - return; - } - } - txn.start(); - // -> authenticated calls - if ( !((method.equalsIgnoreCase( "POST" ) && !(request.getQueryString().equalsIgnoreCase("delete"))) ) ){ - S3AuthParams params = extractRequestHeaders( request ); - authenticateRequest( request, params ); - } + // -> authenticated calls + if ( !((method.equalsIgnoreCase( "POST" ) && !(request.getQueryString().equalsIgnoreCase("delete"))) ) ){ + S3AuthParams params = extractRequestHeaders( request ); + authenticateRequest( request, params ); + } - ServletAction action = routeRequest(request); - if ( action != null ) { - action.execute(request, response); - } - else { - response.setStatus(404); - endResponse(response, "File not found"); - } - txn.close(); + ServletAction action = routeRequest(request); + if ( action != null ) { + action.execute(request, response); + } + else { + response.setStatus(404); + endResponse(response, "File not found"); + } + txn.close(); } catch( InvalidBucketName e) { - logger.error("Unexpected exception " + e.getMessage(), e); - response.setStatus(400); - endResponse(response, "Invalid Bucket Name - " + e.toString()); + logger.error("Unexpected exception " + e.getMessage(), e); + response.setStatus(400); + endResponse(response, "Invalid Bucket Name - " + e.toString()); } catch(PermissionDeniedException e) { - logger.error("Unexpected exception " + e.getMessage(), e); - response.setStatus(403); - endResponse(response, "Access denied - " + e.toString()); + logger.error("Unexpected exception " + e.getMessage(), e); + response.setStatus(403); + endResponse(response, "Access denied - " + e.toString()); } catch(Throwable e) { - logger.error("Unexpected exception " + e.getMessage(), e); - response.setStatus(404); - endResponse(response, "Bad request"); - + logger.error("Unexpected exception " + e.getMessage(), e); + response.setStatus(404); + endResponse(response, "Bad request"); + } finally { - - try { - response.flushBuffer(); - } catch (IOException e) { - logger.error("Unexpected exception " + e.getMessage(), e); - } + + try { + response.flushBuffer(); + } catch (IOException e) { + logger.error("Unexpected exception " + e.getMessage(), e); + } } } - + /** * Provide an easy way to determine the version of the implementation running. * @@ -218,7 +220,7 @@ public class S3RestServlet extends HttpServlet { */ private void cloudS3Version( HttpServletRequest request, HttpServletResponse response ) { String version = new String( "1.04" ); - response.setStatus(200); + response.setStatus(200); endResponse(response, version); } @@ -242,236 +244,236 @@ public class S3RestServlet extends HttpServlet { */ @DB private void setUserKeys( HttpServletRequest request, HttpServletResponse response ) { - String[] accessKey = null; - String[] secretKey = null; - - try { - // -> both these parameters are required + String[] accessKey = null; + String[] secretKey = null; + + try { + // -> both these parameters are required accessKey = request.getParameterValues( "accesskey" ); - if ( null == accessKey || 0 == accessKey.length ) { - response.sendError(530, "Missing accesskey parameter" ); - return; - } + if ( null == accessKey || 0 == accessKey.length ) { + response.sendError(530, "Missing accesskey parameter" ); + return; + } secretKey = request.getParameterValues( "secretkey" ); if ( null == secretKey || 0 == secretKey.length ) { - response.sendError(530, "Missing secretkey parameter" ); - return; + response.sendError(530, "Missing secretkey parameter" ); + return; } } catch( Exception e ) { - logger.error("SetUserKeys exception " + e.getMessage(), e); - response.setStatus(500); - endResponse(response, "SetUserKeys exception " + e.getMessage()); - return; + logger.error("SetUserKeys exception " + e.getMessage(), e); + response.setStatus(500); + endResponse(response, "SetUserKeys exception " + e.getMessage()); + return; } try { // -> use the keys to see if the account actually exists - //ServiceProvider.getInstance().getEC2Engine().validateAccount( accessKey[0], secretKey[0] ); - //UserCredentialsDaoImpl credentialDao = new UserCredentialsDao(); + //ServiceProvider.getInstance().getEC2Engine().validateAccount( accessKey[0], secretKey[0] ); + //UserCredentialsDaoImpl credentialDao = new UserCredentialsDao(); Transaction txn = Transaction.open(Transaction.AWSAPI_DB); txn.start(); - UserCredentialsVO user = new UserCredentialsVO(accessKey[0], secretKey[0]); - user = ucDao.persist(user); - txn.commit(); - txn.close(); - //credentialDao.setUserKeys( accessKey[0], secretKey[0] ); - + UserCredentialsVO user = new UserCredentialsVO(accessKey[0], secretKey[0]); + user = ucDao.persist(user); + txn.commit(); + txn.close(); + //credentialDao.setUserKeys( accessKey[0], secretKey[0] ); + } catch( Exception e ) { - logger.error("SetUserKeys " + e.getMessage(), e); - response.setStatus(401); - endResponse(response, e.toString()); - return; + logger.error("SetUserKeys " + e.getMessage(), e); + response.setStatus(401); + endResponse(response, e.toString()); + return; } - response.setStatus(200); + response.setStatus(200); endResponse(response, "User keys set successfully"); } - + /** * We are using the S3AuthParams class to hide where the header values are coming * from so that the authenticateRequest call can be made from several places. */ public static S3AuthParams extractRequestHeaders( HttpServletRequest request ) { - S3AuthParams params = new S3AuthParams(); - - Enumeration headers = request.getHeaderNames(); - if (null != headers) - { - while( headers.hasMoreElements()) - { - HeaderParam oneHeader = new HeaderParam(); - String headerName = (String)headers.nextElement(); - oneHeader.setName( headerName ); - oneHeader.setValue( request.getHeader( headerName )); - params.addHeader( oneHeader ); - } - } - return params; + S3AuthParams params = new S3AuthParams(); + + Enumeration headers = request.getHeaderNames(); + if (null != headers) + { + while( headers.hasMoreElements()) + { + HeaderParam oneHeader = new HeaderParam(); + String headerName = (String)headers.nextElement(); + oneHeader.setName( headerName ); + oneHeader.setValue( request.getHeader( headerName )); + params.addHeader( oneHeader ); + } + } + return params; } - + public static void authenticateRequest( HttpServletRequest request, S3AuthParams params ) - throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException - { - RestAuth auth = new RestAuth(ServiceProvider.getInstance().getUseSubDomain()); - String AWSAccessKey = null; - String signature = null; - String authorization = null; - - // [A] Is it an annonymous request? - if (null == (authorization = params.getHeader( "Authorization" ))) { + throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException + { + RestAuth auth = new RestAuth(ServiceProvider.getInstance().getUseSubDomain()); + String AWSAccessKey = null; + String signature = null; + String authorization = null; + + // [A] Is it an annonymous request? + if (null == (authorization = params.getHeader( "Authorization" ))) { UserContext.current().initContext(); return; - } - - // [B] Is it an authenticated request? - int offset = authorization.indexOf( "AWS" ); - if (-1 != offset) { - String temp = authorization.substring( offset+3 ).trim(); - offset = temp.indexOf( ":" ); - AWSAccessKey = temp.substring( 0, offset ); - signature = temp.substring( offset+1 ); - } - - // [C] Calculate the signature from the request's headers - auth.setDateHeader( request.getHeader( "Date" )); - auth.setContentTypeHeader( request.getHeader( "Content-Type" )); - auth.setContentMD5Header( request.getHeader( "Content-MD5" )); - auth.setHostHeader( request.getHeader( "Host" )); - auth.setQueryString( request.getQueryString()); - auth.addUriPath( request.getRequestURI()); + } + + // [B] Is it an authenticated request? + int offset = authorization.indexOf( "AWS" ); + if (-1 != offset) { + String temp = authorization.substring( offset+3 ).trim(); + offset = temp.indexOf( ":" ); + AWSAccessKey = temp.substring( 0, offset ); + signature = temp.substring( offset+1 ); + } + + // [C] Calculate the signature from the request's headers + auth.setDateHeader( request.getHeader( "Date" )); + auth.setContentTypeHeader( request.getHeader( "Content-Type" )); + auth.setContentMD5Header( request.getHeader( "Content-MD5" )); + auth.setHostHeader( request.getHeader( "Host" )); + auth.setQueryString( request.getQueryString()); + auth.addUriPath( request.getRequestURI()); + + // -> are their any Amazon specific (i.e. 'x-amz-' ) headers? + HeaderParam[] headers = params.getHeaders(); + for( int i=0; null != headers && i < headers.length; i++ ) + { + String headerName = headers[i].getName(); + String ignoreCase = headerName.toLowerCase(); + if (ignoreCase.startsWith( "x-amz-" )) + auth.addAmazonHeader( headerName + ":" + headers[i].getValue()); + } + + UserInfo info = ServiceProvider.getInstance().getUserInfo(AWSAccessKey); + if (info == null) throw new PermissionDeniedException("Unable to authenticate access key: " + AWSAccessKey); + + try { + if (auth.verifySignature( request.getMethod(), info.getSecretKey(), signature )) { + UserContext.current().initContext(AWSAccessKey, info.getSecretKey(), AWSAccessKey, info.getDescription(), request); + return; + } + + + } catch (SignatureException e) { + throw new PermissionDeniedException(e); + + } catch (UnsupportedEncodingException e) { + throw new PermissionDeniedException(e); + } + throw new PermissionDeniedException("Invalid signature"); + } + + - // -> are their any Amazon specific (i.e. 'x-amz-' ) headers? - HeaderParam[] headers = params.getHeaders(); - for( int i=0; null != headers && i < headers.length; i++ ) - { - String headerName = headers[i].getName(); - String ignoreCase = headerName.toLowerCase(); - if (ignoreCase.startsWith( "x-amz-" )) - auth.addAmazonHeader( headerName + ":" + headers[i].getValue()); - } - - UserInfo info = ServiceProvider.getInstance().getUserInfo(AWSAccessKey); - if (info == null) throw new PermissionDeniedException("Unable to authenticate access key: " + AWSAccessKey); - - try { - if (auth.verifySignature( request.getMethod(), info.getSecretKey(), signature )) { - UserContext.current().initContext(AWSAccessKey, info.getSecretKey(), AWSAccessKey, info.getDescription(), request); - return; - } - - - } catch (SignatureException e) { - throw new PermissionDeniedException(e); - - } catch (UnsupportedEncodingException e) { - throw new PermissionDeniedException(e); - } - throw new PermissionDeniedException("Invalid signature"); - } - - - private ServletAction routeRequest(HttpServletRequest request) { - // URL routing for S3 REST calls. - String pathInfo = request.getPathInfo(); - String bucketName = null; - String key = null; - - String serviceEndpoint = ServiceProvider.getInstance().getServiceEndpoint(); - String host = request.getHeader("Host"); - - // Check for unrecognized forms of URI information in request - - if ( ( pathInfo == null ) || ( pathInfo.indexOf('/') != 0 ) ) - if ( "POST".equalsIgnoreCase(request.getMethod()) ) - // Case where request is POST operation with no pathinfo - // This is the POST alternative to PUT described at s3.amazonaws.com API doc page 141 - { - return routePlainPostRequest (request); - } - - - // Irrespective of whether the requester is using subdomain or full host naming of path expressions - // to buckets, wherever the request is made up of a service endpoint followed by a /, in AWS S3 this always - // conveys a ListAllMyBuckets command - - if ( (serviceEndpoint.equalsIgnoreCase( host )) && (pathInfo.equalsIgnoreCase("/")) ) { - request.setAttribute(S3Constants.BUCKET_ATTR_KEY, "/"); - return new S3BucketAction(); // for ListAllMyBuckets - } + // URL routing for S3 REST calls. + String pathInfo = request.getPathInfo(); + String bucketName = null; + String key = null; - // Because there is a leading / at position 0 of pathInfo, now subtract this to process the remainder - pathInfo = pathInfo.substring(1); - - if (ServiceProvider.getInstance().getUseSubDomain()) - - { - // -> verify the format of the bucket name - int endPos = host.indexOf( ServiceProvider.getInstance().getMasterDomain()); - if ( endPos > 0 ) - { - bucketName = host.substring(0, endPos); - S3Engine.verifyBucketName( bucketName, false ); - request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketName); - } - else request.setAttribute(S3Constants.BUCKET_ATTR_KEY, ""); - - if (pathInfo == null || pathInfo.equalsIgnoreCase("/")) - { - return new S3BucketAction(); - } - else { - String objectKey = pathInfo.substring(1); - request.setAttribute(S3Constants.OBJECT_ATTR_KEY, objectKey); - return new S3ObjectAction(); - } - } - - else - - { - - int endPos = pathInfo.indexOf('/'); // Subsequent / character? - - if (endPos < 1) - { - bucketName = pathInfo; - S3Engine.verifyBucketName( bucketName, false ); - request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketName); - return new S3BucketAction(); - } - else - { - bucketName = pathInfo.substring(0, endPos); - key = pathInfo.substring(endPos + 1); - S3Engine.verifyBucketName( bucketName, false ); - - if (!key.isEmpty()) - { - request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketName); - request.setAttribute(S3Constants.OBJECT_ATTR_KEY, pathInfo.substring(endPos + 1)); - return new S3ObjectAction(); - } - else { - request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketName); - return new S3BucketAction(); - } - } - } + String serviceEndpoint = ServiceProvider.getInstance().getServiceEndpoint(); + String host = request.getHeader("Host"); + + // Check for unrecognized forms of URI information in request + + if ( ( pathInfo == null ) || ( pathInfo.indexOf('/') != 0 ) ) + if ( "POST".equalsIgnoreCase(request.getMethod()) ) + // Case where request is POST operation with no pathinfo + // This is the POST alternative to PUT described at s3.amazonaws.com API doc page 141 + { + return routePlainPostRequest (request); + } + + + // Irrespective of whether the requester is using subdomain or full host naming of path expressions + // to buckets, wherever the request is made up of a service endpoint followed by a /, in AWS S3 this always + // conveys a ListAllMyBuckets command + + if ( (serviceEndpoint.equalsIgnoreCase( host )) && (pathInfo.equalsIgnoreCase("/")) ) { + request.setAttribute(S3Constants.BUCKET_ATTR_KEY, "/"); + return new S3BucketAction(); // for ListAllMyBuckets + } + + // Because there is a leading / at position 0 of pathInfo, now subtract this to process the remainder + pathInfo = pathInfo.substring(1); + + if (ServiceProvider.getInstance().getUseSubDomain()) + + { + // -> verify the format of the bucket name + int endPos = host.indexOf( ServiceProvider.getInstance().getMasterDomain()); + if ( endPos > 0 ) + { + bucketName = host.substring(0, endPos); + S3Engine.verifyBucketName( bucketName, false ); + request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketName); + } + else request.setAttribute(S3Constants.BUCKET_ATTR_KEY, ""); + + if (pathInfo == null || pathInfo.equalsIgnoreCase("/")) + { + return new S3BucketAction(); + } + else { + String objectKey = pathInfo.substring(1); + request.setAttribute(S3Constants.OBJECT_ATTR_KEY, objectKey); + return new S3ObjectAction(); + } + } + + else + + { + + int endPos = pathInfo.indexOf('/'); // Subsequent / character? + + if (endPos < 1) + { + bucketName = pathInfo; + S3Engine.verifyBucketName( bucketName, false ); + request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketName); + return new S3BucketAction(); + } + else + { + bucketName = pathInfo.substring(0, endPos); + key = pathInfo.substring(endPos + 1); + S3Engine.verifyBucketName( bucketName, false ); + + if (!key.isEmpty()) + { + request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketName); + request.setAttribute(S3Constants.OBJECT_ATTR_KEY, pathInfo.substring(endPos + 1)); + return new S3ObjectAction(); + } + else { + request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketName); + return new S3BucketAction(); + } + } + } } - - + + public static void endResponse(HttpServletResponse response, String content) { - try { + try { byte[] data = content.getBytes(); response.setContentLength(data.length); OutputStream os = response.getOutputStream(); os.write(data); os.close(); - } catch(Throwable e) { - logger.error("Unexpected exception " + e.getMessage(), e); - } + } catch(Throwable e) { + logger.error("Unexpected exception " + e.getMessage(), e); + } } public static void writeResponse(HttpServletResponse response, String content) throws IOException { @@ -479,61 +481,61 @@ public class S3RestServlet extends HttpServlet { OutputStream os = response.getOutputStream(); os.write(data); } - + public static void writeResponse(HttpServletResponse response, InputStream is) throws IOException { - byte[] data = new byte[4096]; - int length = 0; - while((length = is.read(data)) > 0) { - response.getOutputStream().write(data, 0, length); - } + byte[] data = new byte[4096]; + int length = 0; + while((length = is.read(data)) > 0) { + response.getOutputStream().write(data, 0, length); + } } // Route for the case where request is POST operation with no pathinfo // This is the POST alternative to PUT described at s3.amazonaws.com API doc, Amazon Simple // Storage Service API Reference API Version 2006-03-01 page 141. // The purpose of the plain POST operation is to add an object to a specified bucket using HTML forms. - -private S3ObjectAction routePlainPostRequest (HttpServletRequest request) + + private S3ObjectAction routePlainPostRequest (HttpServletRequest request) { - // TODO - Remove the unnecessary fields below - // Obtain the mandatory fields from the HTML form or otherwise fail with a logger message - String keyString = request.getParameter("key"); - String metatagString = request.getParameter("x-amz-meta-tag"); - String bucketString = request.getParameter("Bucket"); - String aclString = request.getParameter("acl"); - String fileString = request.getParameter("file"); - - String accessKeyString = request.getParameter("AWSAccessKeyId"); - String signatureString = request.getParameter("Signature"); + // TODO - Remove the unnecessary fields below + // Obtain the mandatory fields from the HTML form or otherwise fail with a logger message + String keyString = request.getParameter("key"); + String metatagString = request.getParameter("x-amz-meta-tag"); + String bucketString = request.getParameter("Bucket"); + String aclString = request.getParameter("acl"); + String fileString = request.getParameter("file"); - // Obtain the discretionary fields from the HTML form - String policyKeyString = request.getParameter("Policy"); - String metauuidString = request.getParameter("x-amz-meta-uuid"); - String redirectString = request.getParameter("redirect"); - - // if none of the above are null then ... - request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketString); - request.setAttribute(S3Constants.OBJECT_ATTR_KEY, keyString); - request.setAttribute(S3Constants.PLAIN_POST_ACCESS_KEY, accessKeyString); - request.setAttribute(S3Constants.PLAIN_POST_SIGNATURE, signatureString); - - // -> authenticated calls - try { - // S3AuthParams params = extractRequestHeaders( request ); - S3AuthParams params = new S3AuthParams(); - HeaderParam headerParam1 = new HeaderParam("accessKey", accessKeyString); - params.addHeader(headerParam1); - HeaderParam headerParam2 = new HeaderParam("secretKey", signatureString); - params.addHeader(headerParam2); - authenticateRequest( request, params ); - } - catch (Exception e) - { logger.warn("Authentication details insufficient"); } + String accessKeyString = request.getParameter("AWSAccessKeyId"); + String signatureString = request.getParameter("Signature"); + + // Obtain the discretionary fields from the HTML form + String policyKeyString = request.getParameter("Policy"); + String metauuidString = request.getParameter("x-amz-meta-uuid"); + String redirectString = request.getParameter("redirect"); + + // if none of the above are null then ... + request.setAttribute(S3Constants.BUCKET_ATTR_KEY, bucketString); + request.setAttribute(S3Constants.OBJECT_ATTR_KEY, keyString); + request.setAttribute(S3Constants.PLAIN_POST_ACCESS_KEY, accessKeyString); + request.setAttribute(S3Constants.PLAIN_POST_SIGNATURE, signatureString); + + // -> authenticated calls + try { + // S3AuthParams params = extractRequestHeaders( request ); + S3AuthParams params = new S3AuthParams(); + HeaderParam headerParam1 = new HeaderParam("accessKey", accessKeyString); + params.addHeader(headerParam1); + HeaderParam headerParam2 = new HeaderParam("secretKey", signatureString); + params.addHeader(headerParam2); + authenticateRequest( request, params ); + } + catch (Exception e) + { logger.warn("Authentication details insufficient"); } + + return new S3ObjectAction(); + + } - return new S3ObjectAction(); - - } - /** * A DIME request is really a SOAP request that we are dealing with, and so its * authentication is the SOAP authentication approach. Since Axis2 does not handle @@ -543,17 +545,17 @@ private S3ObjectAction routePlainPostRequest (HttpServletRequest request) * @param response */ private void processDimeRequest(HttpServletRequest request, HttpServletResponse response) { - S3PutObjectRequest putRequest = null; - S3PutObjectResponse putResponse = null; - int bytesRead = 0; - + S3PutObjectRequest putRequest = null; + S3PutObjectResponse putResponse = null; + int bytesRead = 0; + S3Engine engine = new S3Engine(); - + try { - logRequest(request); - + logRequest(request); + MultiPartDimeInputStream ds = new MultiPartDimeInputStream( request.getInputStream()); - + // -> the first stream MUST be the SOAP party if (ds.nextInputStream()) { @@ -564,26 +566,26 @@ private S3ObjectAction routePlainPostRequest (HttpServletRequest request) ByteArrayInputStream bis = new ByteArrayInputStream( buffer, 0, bytesRead ); putRequest = toEnginePutObjectRequest( bis ); } - + // -> we only need to support a DIME message with two bodyparts if (null != putRequest && ds.nextInputStream()) { - InputStream is = ds.getInputStream(); - putRequest.setData( is ); + InputStream is = ds.getInputStream(); + putRequest.setData( is ); } // -> need to do SOAP level auth here, on failure return the SOAP fault StringBuffer xml = new StringBuffer(); String AWSAccessKey = putRequest.getAccessKey(); - UserInfo info = ServiceProvider.getInstance().getUserInfo(AWSAccessKey); - try - { S3SoapAuth.verifySignature( putRequest.getSignature(), "PutObject", putRequest.getRawTimestamp(), AWSAccessKey, info.getSecretKey()); - - } catch( AxisFault e ) { - String reason = e.toString(); - int start = reason.indexOf( ".AxisFault:" ); - if (-1 != start) reason = reason.substring( start+11 ); - + UserInfo info = ServiceProvider.getInstance().getUserInfo(AWSAccessKey); + try + { S3SoapAuth.verifySignature( putRequest.getSignature(), "PutObject", putRequest.getRawTimestamp(), AWSAccessKey, info.getSecretKey()); + + } catch( AxisFault e ) { + String reason = e.toString(); + int start = reason.indexOf( ".AxisFault:" ); + if (-1 != start) reason = reason.substring( start+11 ); + xml.append( "" ); xml.append( "\n" ); xml.append( "\n" ); @@ -592,15 +594,15 @@ private S3ObjectAction routePlainPostRequest (HttpServletRequest request) xml.append( "" ).append( reason ).append( "\n" ); xml.append( "\n" ); xml.append( "" ); - - endResponse(response, xml.toString()); - return; - } - - // -> PutObject S3 Bucket Policy would be done in the engine.handleRequest() call - UserContext.current().initContext( AWSAccessKey, info.getSecretKey(), AWSAccessKey, "S3 DIME request", request ); + + endResponse(response, xml.toString()); + return; + } + + // -> PutObject S3 Bucket Policy would be done in the engine.handleRequest() call + UserContext.current().initContext( AWSAccessKey, info.getSecretKey(), AWSAccessKey, "S3 DIME request", request ); putResponse = engine.handleRequest( putRequest ); - + xml.append( "" ); xml.append( "" ); xml.append( "" ); @@ -610,24 +612,24 @@ private S3ObjectAction routePlainPostRequest (HttpServletRequest request) xml.append( "").append( DatatypeConverter.printDateTime(putResponse.getLastModified())).append( "" ); xml.append( "" ); xml.append( "" ); - - endResponse(response, xml.toString()); + + endResponse(response, xml.toString()); } catch(PermissionDeniedException e) { - logger.error("Unexpected exception " + e.getMessage(), e); - response.setStatus(403); - endResponse(response, "Access denied"); + logger.error("Unexpected exception " + e.getMessage(), e); + response.setStatus(403); + endResponse(response, "Access denied"); } catch(Throwable e) { - logger.error("Unexpected exception " + e.getMessage(), e); + logger.error("Unexpected exception " + e.getMessage(), e); } finally { } } - + /** * Convert the SOAP XML we extract from the DIME message into our local object. * Here Axis2 is not parsing the SOAP for us. I tried to use the Amazon PutObject @@ -637,240 +639,240 @@ private S3ObjectAction routePlainPostRequest (HttpServletRequest request) * @return * @throws Exception */ - public static S3PutObjectRequest toEnginePutObjectRequest( InputStream is ) throws Exception - { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware( true ); - - DocumentBuilder db = dbf.newDocumentBuilder(); - Document doc = db.parse( is ); - Node parent = null; - Node contents = null; - NodeList children = null; - String temp = null; - String element = null; - int count = 0; + public static S3PutObjectRequest toEnginePutObjectRequest( InputStream is ) throws Exception + { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware( true ); - S3PutObjectRequest request = new S3PutObjectRequest(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse( is ); + Node parent = null; + Node contents = null; + NodeList children = null; + String temp = null; + String element = null; + int count = 0; - // [A] Pull out the simple nodes first - NodeList part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Bucket" ); - if (null != part) - { - if (null != (contents = part.item( 0 ))) - request.setBucketName( contents.getFirstChild().getNodeValue()); - } - part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Key" ); - if (null != part) - { - if (null != (contents = part.item( 0 ))) - request.setKey( contents.getFirstChild().getNodeValue()); - } - part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "ContentLength" ); - if (null != part) - { - if (null != (contents = part.item( 0 ))) - { - String length = contents.getFirstChild().getNodeValue(); - if (null != length) request.setContentLength( Long.decode( length )); - } - } - part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "AWSAccessKeyId" ); - if (null != part) - { - if (null != (contents = part.item( 0 ))) - request.setAccessKey( contents.getFirstChild().getNodeValue()); - } - part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Signature" ); - if (null != part) - { - if (null != (contents = part.item( 0 ))) - request.setSignature( contents.getFirstChild().getNodeValue()); - } - part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Timestamp" ); - if (null != part) - { - if (null != (contents = part.item( 0 ))) - request.setRawTimestamp( contents.getFirstChild().getNodeValue()); - } - part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "StorageClass" ); - if (null != part) - { - if (null != (contents = part.item( 0 ))) - request.setStorageClass( contents.getFirstChild().getNodeValue()); - } - part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Credential" ); - if (null != part) - { - if (null != (contents = part.item( 0 ))) - request.setCredential( contents.getFirstChild().getNodeValue()); - } - - - // [B] Get a list of all 'Metadata' elements - part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Metadata" ); - if (null != part) - { - count = part.getLength(); - S3MetaDataEntry[] metaEntry = new S3MetaDataEntry[ count ]; - - for( int i=0; i < count; i++ ) - { - parent = part.item(i); - metaEntry[i] = new S3MetaDataEntry(); + S3PutObjectRequest request = new S3PutObjectRequest(); - // -> get a list of all the children elements of the 'Metadata' parent element - if (null != (children = parent.getChildNodes())) - { - int numChildren = children.getLength(); - for( int j=0; j < numChildren; j++ ) - { - contents = children.item( j ); - element = contents.getNodeName().trim(); - if ( element.endsWith( "Name" )) - { - temp = contents.getFirstChild().getNodeValue(); - if (null != temp) metaEntry[i].setName( temp ); - } - else if (element.endsWith( "Value" )) - { - temp = contents.getFirstChild().getNodeValue(); - if (null != temp) metaEntry[i].setValue( temp ); - } - } - } - } - request.setMetaEntries( metaEntry ); - } + // [A] Pull out the simple nodes first + NodeList part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Bucket" ); + if (null != part) + { + if (null != (contents = part.item( 0 ))) + request.setBucketName( contents.getFirstChild().getNodeValue()); + } + part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Key" ); + if (null != part) + { + if (null != (contents = part.item( 0 ))) + request.setKey( contents.getFirstChild().getNodeValue()); + } + part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "ContentLength" ); + if (null != part) + { + if (null != (contents = part.item( 0 ))) + { + String length = contents.getFirstChild().getNodeValue(); + if (null != length) request.setContentLength( Long.decode( length )); + } + } + part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "AWSAccessKeyId" ); + if (null != part) + { + if (null != (contents = part.item( 0 ))) + request.setAccessKey( contents.getFirstChild().getNodeValue()); + } + part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Signature" ); + if (null != part) + { + if (null != (contents = part.item( 0 ))) + request.setSignature( contents.getFirstChild().getNodeValue()); + } + part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Timestamp" ); + if (null != part) + { + if (null != (contents = part.item( 0 ))) + request.setRawTimestamp( contents.getFirstChild().getNodeValue()); + } + part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "StorageClass" ); + if (null != part) + { + if (null != (contents = part.item( 0 ))) + request.setStorageClass( contents.getFirstChild().getNodeValue()); + } + part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Credential" ); + if (null != part) + { + if (null != (contents = part.item( 0 ))) + request.setCredential( contents.getFirstChild().getNodeValue()); + } - // [C] Get a list of all Grant elements in an AccessControlList - part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Grant" ); - if (null != part) - { - S3AccessControlList engineAcl = new S3AccessControlList(); - count = part.getLength(); + // [B] Get a list of all 'Metadata' elements + part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Metadata" ); + if (null != part) + { + count = part.getLength(); + S3MetaDataEntry[] metaEntry = new S3MetaDataEntry[ count ]; + for( int i=0; i < count; i++ ) { - parent = part.item(i); - S3Grant engineGrant = new S3Grant(); + parent = part.item(i); + metaEntry[i] = new S3MetaDataEntry(); - // -> get a list of all the children elements of the 'Grant' parent element - if (null != (children = parent.getChildNodes())) - { - int numChildren = children.getLength(); - for( int j=0; j < numChildren; j++ ) - { - contents = children.item( j ); - element = contents.getNodeName().trim(); - if ( element.endsWith( "Grantee" )) - { - NamedNodeMap attbs = contents.getAttributes(); - if (null != attbs) - { - Node type = attbs.getNamedItemNS( "http://www.w3.org/2001/XMLSchema-instance", "type" ); - if ( null != type ) - temp = type.getFirstChild().getNodeValue().trim(); - else temp = null; - - if ( null != temp && temp.equalsIgnoreCase( "CanonicalUser" )) - { - engineGrant.setGrantee(SAcl.GRANTEE_USER); - engineGrant.setCanonicalUserID( getChildNodeValue( contents, "ID" )); - } - else throw new UnsupportedOperationException( "Missing http://www.w3.org/2001/XMLSchema-instance:type value" ); - } - } - else if (element.endsWith( "Permission" )) - { - temp = contents.getFirstChild().getNodeValue().trim(); - if (temp.equalsIgnoreCase("READ" )) engineGrant.setPermission(SAcl.PERMISSION_READ); - else if (temp.equalsIgnoreCase("WRITE" )) engineGrant.setPermission(SAcl.PERMISSION_WRITE); - else if (temp.equalsIgnoreCase("READ_ACP" )) engineGrant.setPermission(SAcl.PERMISSION_READ_ACL); - else if (temp.equalsIgnoreCase("WRITE_ACP" )) engineGrant.setPermission(SAcl.PERMISSION_WRITE_ACL); - else if (temp.equalsIgnoreCase("FULL_CONTROL")) engineGrant.setPermission(SAcl.PERMISSION_FULL); - else throw new UnsupportedOperationException( "Unsupported permission: " + temp ); - } - } - engineAcl.addGrant( engineGrant ); - } + // -> get a list of all the children elements of the 'Metadata' parent element + if (null != (children = parent.getChildNodes())) + { + int numChildren = children.getLength(); + for( int j=0; j < numChildren; j++ ) + { + contents = children.item( j ); + element = contents.getNodeName().trim(); + if ( element.endsWith( "Name" )) + { + temp = contents.getFirstChild().getNodeValue(); + if (null != temp) metaEntry[i].setName( temp ); + } + else if (element.endsWith( "Value" )) + { + temp = contents.getFirstChild().getNodeValue(); + if (null != temp) metaEntry[i].setValue( temp ); + } + } + } } - request.setAcl( engineAcl ); - } - return request; - } - - /** - * Have to deal with XML with and without namespaces. - */ - public static NodeList getElement( Document doc, String namespace, String tagName ) - { - NodeList part = doc.getElementsByTagNameNS( namespace, tagName ); - if (null == part || 0 == part.getLength()) part = doc.getElementsByTagName( tagName ); - - return part; - } + request.setMetaEntries( metaEntry ); + } - /** - * Looking for the value of a specific child of the given parent node. - * - * @param parent - * @param childName - * @return - */ - private static String getChildNodeValue( Node parent, String childName ) - { - NodeList children = null; - Node element = null; + // [C] Get a list of all Grant elements in an AccessControlList + part = getElement( doc, "http://s3.amazonaws.com/doc/2006-03-01/", "Grant" ); + if (null != part) + { + S3AccessControlList engineAcl = new S3AccessControlList(); + + count = part.getLength(); + for( int i=0; i < count; i++ ) + { + parent = part.item(i); + S3Grant engineGrant = new S3Grant(); + + // -> get a list of all the children elements of the 'Grant' parent element + if (null != (children = parent.getChildNodes())) + { + int numChildren = children.getLength(); + for( int j=0; j < numChildren; j++ ) + { + contents = children.item( j ); + element = contents.getNodeName().trim(); + if ( element.endsWith( "Grantee" )) + { + NamedNodeMap attbs = contents.getAttributes(); + if (null != attbs) + { + Node type = attbs.getNamedItemNS( "http://www.w3.org/2001/XMLSchema-instance", "type" ); + if ( null != type ) + temp = type.getFirstChild().getNodeValue().trim(); + else temp = null; + + if ( null != temp && temp.equalsIgnoreCase( "CanonicalUser" )) + { + engineGrant.setGrantee(SAcl.GRANTEE_USER); + engineGrant.setCanonicalUserID( getChildNodeValue( contents, "ID" )); + } + else throw new UnsupportedOperationException( "Missing http://www.w3.org/2001/XMLSchema-instance:type value" ); + } + } + else if (element.endsWith( "Permission" )) + { + temp = contents.getFirstChild().getNodeValue().trim(); + if (temp.equalsIgnoreCase("READ" )) engineGrant.setPermission(SAcl.PERMISSION_READ); + else if (temp.equalsIgnoreCase("WRITE" )) engineGrant.setPermission(SAcl.PERMISSION_WRITE); + else if (temp.equalsIgnoreCase("READ_ACP" )) engineGrant.setPermission(SAcl.PERMISSION_READ_ACL); + else if (temp.equalsIgnoreCase("WRITE_ACP" )) engineGrant.setPermission(SAcl.PERMISSION_WRITE_ACL); + else if (temp.equalsIgnoreCase("FULL_CONTROL")) engineGrant.setPermission(SAcl.PERMISSION_FULL); + else throw new UnsupportedOperationException( "Unsupported permission: " + temp ); + } + } + engineAcl.addGrant( engineGrant ); + } + } + request.setAcl( engineAcl ); + } + return request; + } + + /** + * Have to deal with XML with and without namespaces. + */ + public static NodeList getElement( Document doc, String namespace, String tagName ) + { + NodeList part = doc.getElementsByTagNameNS( namespace, tagName ); + if (null == part || 0 == part.getLength()) part = doc.getElementsByTagName( tagName ); + + return part; + } + + /** + * Looking for the value of a specific child of the given parent node. + * + * @param parent + * @param childName + * @return + */ + private static String getChildNodeValue( Node parent, String childName ) + { + NodeList children = null; + Node element = null; + + if (null != (children = parent.getChildNodes())) + { + int numChildren = children.getLength(); + for( int i=0; i < numChildren; i++ ) + { + if (null != (element = children.item( i ))) + { + // -> name may have a namespace on it + String name = element.getNodeName().trim(); + if ( name.endsWith( childName )) + { + String value = element.getFirstChild().getNodeValue(); + if (null != value) value = value.trim(); + return value; + } + } + } + } + return null; + } - if (null != (children = parent.getChildNodes())) - { - int numChildren = children.getLength(); - for( int i=0; i < numChildren; i++ ) - { - if (null != (element = children.item( i ))) - { - // -> name may have a namespace on it - String name = element.getNodeName().trim(); - if ( name.endsWith( childName )) - { - String value = element.getFirstChild().getNodeValue(); - if (null != value) value = value.trim(); - return value; - } - } - } - } - return null; - } - private void logRequest(HttpServletRequest request) { - if(logger.isInfoEnabled()) { - logger.info("Request method: " + request.getMethod()); - logger.info("Request contextPath: " + request.getContextPath()); - logger.info("Request pathInfo: " + request.getPathInfo()); - logger.info("Request pathTranslated: " + request.getPathTranslated()); - logger.info("Request queryString: " + request.getQueryString()); - logger.info("Request requestURI: " + request.getRequestURI()); - logger.info("Request requestURL: " + request.getRequestURL()); - logger.info("Request servletPath: " + request.getServletPath()); - Enumeration headers = request.getHeaderNames(); - if(headers != null) { - while(headers.hasMoreElements()) { - Object headerName = headers.nextElement(); - logger.info("Request header " + headerName + ":" + request.getHeader((String)headerName)); - } - } - - Enumeration params = request.getParameterNames(); - if(params != null) { - while(params.hasMoreElements()) { - Object paramName = params.nextElement(); - logger.info("Request parameter " + paramName + ":" + - request.getParameter((String)paramName)); - } - } - logger.info( "- End of request -" ); - } + if(logger.isInfoEnabled()) { + logger.info("Request method: " + request.getMethod()); + logger.info("Request contextPath: " + request.getContextPath()); + logger.info("Request pathInfo: " + request.getPathInfo()); + logger.info("Request pathTranslated: " + request.getPathTranslated()); + logger.info("Request queryString: " + request.getQueryString()); + logger.info("Request requestURI: " + request.getRequestURI()); + logger.info("Request requestURL: " + request.getRequestURL()); + logger.info("Request servletPath: " + request.getServletPath()); + Enumeration headers = request.getHeaderNames(); + if(headers != null) { + while(headers.hasMoreElements()) { + Object headerName = headers.nextElement(); + logger.info("Request header " + headerName + ":" + request.getHeader((String)headerName)); + } + } + + Enumeration params = request.getParameterNames(); + if(params != null) { + while(params.hasMoreElements()) { + Object paramName = params.nextElement(); + logger.info("Request parameter " + paramName + ":" + + request.getParameter((String)paramName)); + } + } + logger.info( "- End of request -" ); + } } } diff --git a/awsapi/src/com/cloud/bridge/service/controller/s3/S3BucketAction.java b/awsapi/src/com/cloud/bridge/service/controller/s3/S3BucketAction.java index 8f77916f750..c98de34a698 100644 --- a/awsapi/src/com/cloud/bridge/service/controller/s3/S3BucketAction.java +++ b/awsapi/src/com/cloud/bridge/service/controller/s3/S3BucketAction.java @@ -21,19 +21,16 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; -import java.io.PrintWriter; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; -import java.util.ArrayList; import java.text.SimpleDateFormat; import java.util.Calendar; -import java.util.List; +import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.bind.DatatypeConverter; - import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.stream.XMLStreamException; @@ -52,24 +49,17 @@ import com.cloud.bridge.io.MTOMAwareResultStreamWriter; import com.cloud.bridge.model.BucketPolicyVO; import com.cloud.bridge.model.SAcl; import com.cloud.bridge.model.SAclVO; -import com.cloud.bridge.model.SBucket; import com.cloud.bridge.model.SBucketVO; -import com.cloud.bridge.model.SHost; import com.cloud.bridge.persist.dao.BucketPolicyDao; -import com.cloud.bridge.persist.dao.BucketPolicyDaoImpl; import com.cloud.bridge.persist.dao.MultipartLoadDao; -import com.cloud.bridge.persist.dao.SAclDaoImpl; import com.cloud.bridge.persist.dao.SBucketDao; -import com.cloud.bridge.persist.dao.SBucketDaoImpl; import com.cloud.bridge.service.S3Constants; import com.cloud.bridge.service.S3RestServlet; -import com.cloud.bridge.service.controller.s3.ServiceProvider; import com.cloud.bridge.service.UserContext; import com.cloud.bridge.service.core.s3.S3AccessControlList; import com.cloud.bridge.service.core.s3.S3AccessControlPolicy; -import com.cloud.bridge.service.core.s3.S3AuthParams; -import com.cloud.bridge.service.core.s3.S3BucketAdapter; import com.cloud.bridge.service.core.s3.S3BucketPolicy; +import com.cloud.bridge.service.core.s3.S3BucketPolicy.PolicyAccess; import com.cloud.bridge.service.core.s3.S3CanonicalUser; import com.cloud.bridge.service.core.s3.S3CreateBucketConfiguration; import com.cloud.bridge.service.core.s3.S3CreateBucketRequest; @@ -86,1083 +76,1077 @@ import com.cloud.bridge.service.core.s3.S3ListBucketObjectEntry; import com.cloud.bridge.service.core.s3.S3ListBucketRequest; import com.cloud.bridge.service.core.s3.S3ListBucketResponse; import com.cloud.bridge.service.core.s3.S3MultipartUpload; +import com.cloud.bridge.service.core.s3.S3PolicyAction.PolicyActions; +import com.cloud.bridge.service.core.s3.S3PolicyCondition.ConditionKeys; import com.cloud.bridge.service.core.s3.S3PolicyContext; import com.cloud.bridge.service.core.s3.S3Response; import com.cloud.bridge.service.core.s3.S3SetBucketAccessControlPolicyRequest; -import com.cloud.bridge.service.core.s3.S3BucketPolicy.PolicyAccess; -import com.cloud.bridge.service.core.s3.S3PolicyAction.PolicyActions; -import com.cloud.bridge.service.core.s3.S3PolicyCondition.ConditionKeys; -import com.cloud.bridge.service.exception.InvalidBucketName; import com.cloud.bridge.service.exception.InvalidRequestContentException; import com.cloud.bridge.service.exception.NetworkIOException; import com.cloud.bridge.service.exception.NoSuchObjectException; import com.cloud.bridge.service.exception.ObjectAlreadyExistsException; -import com.cloud.bridge.service.exception.OutOfServiceException; import com.cloud.bridge.service.exception.PermissionDeniedException; import com.cloud.bridge.util.Converter; -import com.cloud.bridge.util.DateHelper; -import com.cloud.bridge.util.HeaderParam; +import com.cloud.bridge.util.OrderedPair; import com.cloud.bridge.util.PolicyParser; import com.cloud.bridge.util.StringHelper; -import com.cloud.bridge.util.OrderedPair; -import com.cloud.bridge.util.Triple; import com.cloud.bridge.util.XSerializer; import com.cloud.bridge.util.XSerializerXmlAdapter; import com.cloud.bridge.util.XmlHelper; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.Transaction; public class S3BucketAction implements ServletAction { protected final static Logger logger = Logger.getLogger(S3BucketAction.class); - protected final BucketPolicyDao bPolicyDao = ComponentLocator.inject(BucketPolicyDaoImpl.class); - protected final SBucketDao bucketDao = ComponentLocator.inject(SBucketDaoImpl.class); - + @Inject BucketPolicyDao bPolicyDao; + @Inject SBucketDao bucketDao; + private DocumentBuilderFactory dbf = null; - public S3BucketAction() { - dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware( true ); + public S3BucketAction() { + dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware( true ); - } - - public void execute(HttpServletRequest request, HttpServletResponse response) - throws IOException, XMLStreamException - { - String method = request.getMethod(); - String queryString = request.getQueryString(); - - if ( method.equalsIgnoreCase("PUT")) - { - if ( queryString != null && queryString.length() > 0 ) - { - if ( queryString.startsWith("acl")) { - executePutBucketAcl(request, response); - return; - } - else if (queryString.startsWith("versioning")) { - executePutBucketVersioning(request, response); - return; - } - else if (queryString.startsWith("policy")) { - executePutBucketPolicy(request, response); - return; - } - else if (queryString.startsWith("logging")) { - executePutBucketLogging(request, response); - return; - } - else if (queryString.startsWith("website")) { - executePutBucketWebsite(request, response); - return; - } - } - executePutBucket(request, response); - } - else if(method.equalsIgnoreCase("GET") || method.equalsIgnoreCase("HEAD")) - { - if (queryString != null && queryString.length() > 0) - { - if ( queryString.startsWith("acl")) { - executeGetBucketAcl(request, response); - return; - } - else if (queryString.startsWith("versioning")) { - executeGetBucketVersioning(request, response); - return; - } - else if (queryString.contains("versions")) { - executeGetBucketObjectVersions(request, response); - return; - } - else if (queryString.startsWith("location")) { - executeGetBucketLocation(request, response); - return; - } - else if (queryString.startsWith("uploads")) { - executeListMultipartUploads(request, response); - return; - } - else if (queryString.startsWith("policy")) { - executeGetBucketPolicy(request, response); - return; - } - else if (queryString.startsWith("logging")) { - executeGetBucketLogging(request, response); - return; - } - else if (queryString.startsWith("website")) { - executeGetBucketWebsite(request, response); - return; - } - } - - String bucketAtr = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - if ( bucketAtr.equals( "/" )) - executeGetAllBuckets(request, response); - else executeGetBucket(request, response); - } - else if (method.equalsIgnoreCase("DELETE")) - { - if (queryString != null && queryString.length() > 0) - { - if ( queryString.startsWith("policy")) { - executeDeleteBucketPolicy(request, response); - return; - } - else if (queryString.startsWith("website")) { - executeDeleteBucketWebsite(request, response); - return; - } + } - } - executeDeleteBucket(request, response); - } - else if ( (method.equalsIgnoreCase("POST")) && (queryString.equalsIgnoreCase("delete")) ) - { - executeMultiObjectDelete(request, response); - } - else throw new IllegalArgumentException("Unsupported method in REST request"); - } - - - -private void executeMultiObjectDelete(HttpServletRequest request, HttpServletResponse response) throws IOException{ + @Override + public void execute(HttpServletRequest request, HttpServletResponse response) + throws IOException, XMLStreamException + { + String method = request.getMethod(); + String queryString = request.getQueryString(); - int contentLength = request.getContentLength(); - StringBuffer xmlDeleteResponse = null; - boolean quite = true; - - if(contentLength > 0) - { - InputStream is = null; - String versionID =null; - try { - is = request.getInputStream(); - String xml = StringHelper.stringFromStream(is); - String elements[] = {"Key","VersionId"}; - Document doc = XmlHelper.parse(xml); - Node node = XmlHelper.getRootNode(doc); - - if(node == null) { - System.out.println("Invalid XML document, no root element"); - return; - } - - xmlDeleteResponse = new StringBuffer("" + - ""); - - String bucket = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + if ( method.equalsIgnoreCase("PUT")) + { + if ( queryString != null && queryString.length() > 0 ) + { + if ( queryString.startsWith("acl")) { + executePutBucketAcl(request, response); + return; + } + else if (queryString.startsWith("versioning")) { + executePutBucketVersioning(request, response); + return; + } + else if (queryString.startsWith("policy")) { + executePutBucketPolicy(request, response); + return; + } + else if (queryString.startsWith("logging")) { + executePutBucketLogging(request, response); + return; + } + else if (queryString.startsWith("website")) { + executePutBucketWebsite(request, response); + return; + } + } + executePutBucket(request, response); + } + else if(method.equalsIgnoreCase("GET") || method.equalsIgnoreCase("HEAD")) + { + if (queryString != null && queryString.length() > 0) + { + if ( queryString.startsWith("acl")) { + executeGetBucketAcl(request, response); + return; + } + else if (queryString.startsWith("versioning")) { + executeGetBucketVersioning(request, response); + return; + } + else if (queryString.contains("versions")) { + executeGetBucketObjectVersions(request, response); + return; + } + else if (queryString.startsWith("location")) { + executeGetBucketLocation(request, response); + return; + } + else if (queryString.startsWith("uploads")) { + executeListMultipartUploads(request, response); + return; + } + else if (queryString.startsWith("policy")) { + executeGetBucketPolicy(request, response); + return; + } + else if (queryString.startsWith("logging")) { + executeGetBucketLogging(request, response); + return; + } + else if (queryString.startsWith("website")) { + executeGetBucketWebsite(request, response); + return; + } + } - S3DeleteObjectRequest engineRequest = new S3DeleteObjectRequest(); - engineRequest.setBucketName( bucket ); - is.close(); - - doc.getDocumentElement().normalize(); - NodeList qList = doc.getElementsByTagName("Quiet"); + String bucketAtr = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + if ( bucketAtr.equals( "/" )) + executeGetAllBuckets(request, response); + else executeGetBucket(request, response); + } + else if (method.equalsIgnoreCase("DELETE")) + { + if (queryString != null && queryString.length() > 0) + { + if ( queryString.startsWith("policy")) { + executeDeleteBucketPolicy(request, response); + return; + } + else if (queryString.startsWith("website")) { + executeDeleteBucketWebsite(request, response); + return; + } - if (qList.getLength() == 1 ) { - Node qNode= qList.item(0); - if ( qNode.getFirstChild().getNodeValue().equalsIgnoreCase("true") == false ) - quite = false; - - logger.debug("Quite value :" + qNode.getFirstChild().getNodeValue()); - } - - NodeList objList = doc.getElementsByTagName("Object"); - - for (int i = 0; i < objList.getLength(); i++) { + } + executeDeleteBucket(request, response); + } + else if ( (method.equalsIgnoreCase("POST")) && (queryString.equalsIgnoreCase("delete")) ) + { + executeMultiObjectDelete(request, response); + } + else throw new IllegalArgumentException("Unsupported method in REST request"); + } - Node key = objList.item(i); - NodeList key_data = key.getChildNodes(); - - if (key.getNodeType() == Node.ELEMENT_NODE) { - Element eElement = (Element) key; - String key_name = getTagValue(elements[0], eElement); - engineRequest.setBucketName(bucket); - engineRequest.setKey(key_name); - if (key_data.getLength() == 2) { - versionID = getTagValue(elements[1], eElement); - engineRequest.setVersion(versionID); - } - S3Response engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest( engineRequest ); - int resultCode = engineResponse.getResultCode(); - String resutlDesc = engineResponse.getResultDescription(); - if(resultCode == 204) { - if (quite) { // show response depending on quite/verbose - xmlDeleteResponse.append(""+key_name+""); - if (resutlDesc != null) - xmlDeleteResponse.append(resutlDesc); - xmlDeleteResponse.append(""); - } - } - else { - logger.debug("Error in delete ::" + key_name + " eng response:: " + engineResponse.getResultDescription()); - xmlDeleteResponse.append(""+key_name+"" ); - if (resutlDesc != null) - xmlDeleteResponse.append(resutlDesc); - xmlDeleteResponse.append(""); - } - - - } - } - - String version = engineRequest.getVersion(); - if (null != version) response.addHeader( "x-amz-version-id", version ); - - - } catch (IOException e) { - logger.error("Unable to read request data due to " + e.getMessage(), e); - throw new NetworkIOException(e); - - } finally { - if(is != null) is.close(); - } + private void executeMultiObjectDelete(HttpServletRequest request, HttpServletResponse response) throws IOException{ - xmlDeleteResponse.append(""); - - } - response.setStatus(200); - response.setContentType("text/xml; charset=UTF-8"); - S3RestServlet.endResponse(response, xmlDeleteResponse.toString()); - - } + int contentLength = request.getContentLength(); + StringBuffer xmlDeleteResponse = null; + boolean quite = true; - private String getTagValue(String sTag, Element eElement) { - - NodeList nlList = eElement.getElementsByTagName(sTag).item(0).getChildNodes(); - Node nValue = (Node) nlList.item(0); - return nValue.getNodeValue(); - } - - - /** - * In order to support a policy on the "s3:CreateBucket" action we must be able to set and get - * policies before a bucket is actually created. - * - * @param request - * @param response - * @throws IOException - */ - private void executePutBucketPolicy(HttpServletRequest request, HttpServletResponse response) throws IOException - { - String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String policy = streamToString( request.getInputStream()); - - // [A] Is there an owner of an existing policy or bucket? + if(contentLength > 0) + { + InputStream is = null; + String versionID =null; + try { + is = request.getInputStream(); + String xml = StringHelper.stringFromStream(is); + String elements[] = {"Key","VersionId"}; + Document doc = XmlHelper.parse(xml); + Node node = XmlHelper.getRootNode(doc); + + if(node == null) { + System.out.println("Invalid XML document, no root element"); + return; + } + + xmlDeleteResponse = new StringBuffer("" + + ""); + + String bucket = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + + S3DeleteObjectRequest engineRequest = new S3DeleteObjectRequest(); + engineRequest.setBucketName( bucket ); + is.close(); + + doc.getDocumentElement().normalize(); + NodeList qList = doc.getElementsByTagName("Quiet"); + + if (qList.getLength() == 1 ) { + Node qNode= qList.item(0); + if ( qNode.getFirstChild().getNodeValue().equalsIgnoreCase("true") == false ) + quite = false; + + logger.debug("Quite value :" + qNode.getFirstChild().getNodeValue()); + } + + NodeList objList = doc.getElementsByTagName("Object"); + + for (int i = 0; i < objList.getLength(); i++) { + + Node key = objList.item(i); + NodeList key_data = key.getChildNodes(); + + if (key.getNodeType() == Node.ELEMENT_NODE) { + Element eElement = (Element) key; + String key_name = getTagValue(elements[0], eElement); + engineRequest.setBucketName(bucket); + engineRequest.setKey(key_name); + + if (key_data.getLength() == 2) { + versionID = getTagValue(elements[1], eElement); + engineRequest.setVersion(versionID); + } + + S3Response engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest( engineRequest ); + int resultCode = engineResponse.getResultCode(); + String resutlDesc = engineResponse.getResultDescription(); + if(resultCode == 204) { + if (quite) { // show response depending on quite/verbose + xmlDeleteResponse.append(""+key_name+""); + if (resutlDesc != null) + xmlDeleteResponse.append(resutlDesc); + xmlDeleteResponse.append(""); + } + } + else { + logger.debug("Error in delete ::" + key_name + " eng response:: " + engineResponse.getResultDescription()); + xmlDeleteResponse.append(""+key_name+"" ); + if (resutlDesc != null) + xmlDeleteResponse.append(resutlDesc); + xmlDeleteResponse.append(""); + } + + + } + } + + String version = engineRequest.getVersion(); + if (null != version) response.addHeader( "x-amz-version-id", version ); + + + } catch (IOException e) { + logger.error("Unable to read request data due to " + e.getMessage(), e); + throw new NetworkIOException(e); + + } finally { + if(is != null) is.close(); + } + + xmlDeleteResponse.append(""); + + } + response.setStatus(200); + response.setContentType("text/xml; charset=UTF-8"); + S3RestServlet.endResponse(response, xmlDeleteResponse.toString()); + + } + + private String getTagValue(String sTag, Element eElement) { + + NodeList nlList = eElement.getElementsByTagName(sTag).item(0).getChildNodes(); + Node nValue = nlList.item(0); + return nValue.getNodeValue(); + } + + + /** + * In order to support a policy on the "s3:CreateBucket" action we must be able to set and get + * policies before a bucket is actually created. + * + * @param request + * @param response + * @throws IOException + */ + private void executePutBucketPolicy(HttpServletRequest request, HttpServletResponse response) throws IOException + { + String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String policy = streamToString( request.getInputStream()); + + // [A] Is there an owner of an existing policy or bucket? SBucketVO bucket = bucketDao.getByName( bucketName ); String owner = null; - + if ( null != bucket ) { owner = bucket.getOwnerCanonicalId(); } else { try { - owner = bPolicyDao.getByName(bucketName).getOwnerCanonicalID(); - } - catch( Exception e ) {} + owner = bPolicyDao.getByName(bucketName).getOwnerCanonicalID(); + } + catch( Exception e ) {} } - - // [B] "The bucket owner by default has permissions to attach bucket policies to their buckets using PUT Bucket policy." - // -> the bucket owner may want to restrict the IP address from where this can be executed - String client = UserContext.current().getCanonicalUserId(); - S3PolicyContext context = new S3PolicyContext( - PolicyActions.PutBucketPolicy, bucketName); - - switch (S3Engine.verifyPolicy(context)) { - case ALLOW: - break; - case DEFAULT_DENY: - if (null != owner && !client.equals(owner)) { - response.setStatus(405); - return; - } - break; - case DENY: - response.setStatus(403); - return; - } - Transaction txn = Transaction.open(Transaction.AWSAPI_DB); - // [B] Place the policy into the database over writting an existing policy - try { - // -> first make sure that the policy is valid by parsing it - PolicyParser parser = new PolicyParser(); - S3BucketPolicy sbp = parser.parse( policy, bucketName ); - bPolicyDao.deletePolicy(bucketName); - - if (null != policy && !policy.isEmpty()) { - BucketPolicyVO bpolicy = new BucketPolicyVO(bucketName, client, policy); - bpolicy = bPolicyDao.persist(bpolicy); - //policyDao.addPolicy( bucketName, client, policy ); - } - - if (null != sbp) ServiceProvider.getInstance().setBucketPolicy( bucketName, sbp ); - response.setStatus(200); - txn.commit(); - txn.close(); - } - catch( PermissionDeniedException e ) { - logger.error("Put Bucket Policy failed due to " + e.getMessage(), e); - throw e; - } - catch( ParseException e ) { - logger.error("Put Bucket Policy failed due to " + e.getMessage(), e); - throw new PermissionDeniedException( e.toString()); - } - catch( Exception e ) { - logger.error("Put Bucket Policy failed due to " + e.getMessage(), e); - response.setStatus(500); - } - } - - private void executeGetBucketPolicy(HttpServletRequest request, HttpServletResponse response) - { - String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + // [B] "The bucket owner by default has permissions to attach bucket policies to their buckets using PUT Bucket policy." + // -> the bucket owner may want to restrict the IP address from where this can be executed + String client = UserContext.current().getCanonicalUserId(); + S3PolicyContext context = new S3PolicyContext( + PolicyActions.PutBucketPolicy, bucketName); - // [A] Is there an owner of an existing policy or bucket? - SBucketVO bucket = bucketDao.getByName(bucketName); - String owner = null; + switch (S3Engine.verifyPolicy(context)) { + case ALLOW: + break; - if (null != bucket) { - owner = bucket.getOwnerCanonicalId(); - } else { - try { - owner = bPolicyDao.getByName(bucketName).getOwnerCanonicalID(); - } catch (Exception e) { - } - } + case DEFAULT_DENY: + if (null != owner && !client.equals(owner)) { + response.setStatus(405); + return; + } + break; + case DENY: + response.setStatus(403); + return; + } + Transaction txn = Transaction.open(Transaction.AWSAPI_DB); + // [B] Place the policy into the database over writting an existing policy + try { + // -> first make sure that the policy is valid by parsing it + PolicyParser parser = new PolicyParser(); + S3BucketPolicy sbp = parser.parse( policy, bucketName ); + bPolicyDao.deletePolicy(bucketName); - // [B] - // "The bucket owner by default has permissions to retrieve bucket policies using GET Bucket policy." - // -> the bucket owner may want to restrict the IP address from where - // this can be executed - String client = UserContext.current().getCanonicalUserId(); - S3PolicyContext context = new S3PolicyContext( - PolicyActions.GetBucketPolicy, bucketName); - switch (S3Engine.verifyPolicy(context)) { - case ALLOW: - break; + if (null != policy && !policy.isEmpty()) { + BucketPolicyVO bpolicy = new BucketPolicyVO(bucketName, client, policy); + bpolicy = bPolicyDao.persist(bpolicy); + //policyDao.addPolicy( bucketName, client, policy ); + } - case DEFAULT_DENY: - if (null != owner && !client.equals(owner)) { - response.setStatus(405); - return; - } - break; + if (null != sbp) ServiceProvider.getInstance().setBucketPolicy( bucketName, sbp ); + response.setStatus(200); + txn.commit(); + txn.close(); + } + catch( PermissionDeniedException e ) { + logger.error("Put Bucket Policy failed due to " + e.getMessage(), e); + throw e; + } + catch( ParseException e ) { + logger.error("Put Bucket Policy failed due to " + e.getMessage(), e); + throw new PermissionDeniedException( e.toString()); + } + catch( Exception e ) { + logger.error("Put Bucket Policy failed due to " + e.getMessage(), e); + response.setStatus(500); + } + } - case DENY: - response.setStatus(403); - return; - } + private void executeGetBucketPolicy(HttpServletRequest request, HttpServletResponse response) + { + String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - // [B] Pull the policy from the database if one exists - try { - String policy = bPolicyDao.getByName(bucketName).getPolicy(); - if (null == policy) { - response.setStatus(404); - } else { - response.setStatus(200); - response.setContentType("application/json"); - S3RestServlet.endResponse(response, policy); - } - } catch (Exception e) { - logger.error("Get Bucket Policy failed due to " + e.getMessage(), e); - response.setStatus(500); - } + // [A] Is there an owner of an existing policy or bucket? + SBucketVO bucket = bucketDao.getByName(bucketName); + String owner = null; + + if (null != bucket) { + owner = bucket.getOwnerCanonicalId(); + } else { + try { + owner = bPolicyDao.getByName(bucketName).getOwnerCanonicalID(); + } catch (Exception e) { + } + } + + // [B] + // "The bucket owner by default has permissions to retrieve bucket policies using GET Bucket policy." + // -> the bucket owner may want to restrict the IP address from where + // this can be executed + String client = UserContext.current().getCanonicalUserId(); + S3PolicyContext context = new S3PolicyContext( + PolicyActions.GetBucketPolicy, bucketName); + switch (S3Engine.verifyPolicy(context)) { + case ALLOW: + break; + + case DEFAULT_DENY: + if (null != owner && !client.equals(owner)) { + response.setStatus(405); + return; + } + break; + + case DENY: + response.setStatus(403); + return; + } + + // [B] Pull the policy from the database if one exists + try { + String policy = bPolicyDao.getByName(bucketName).getPolicy(); + if (null == policy) { + response.setStatus(404); + } else { + response.setStatus(200); + response.setContentType("application/json"); + S3RestServlet.endResponse(response, policy); + } + } catch (Exception e) { + logger.error("Get Bucket Policy failed due to " + e.getMessage(), e); + response.setStatus(500); + } } private void executeDeleteBucketPolicy(HttpServletRequest request, - HttpServletResponse response) { - String bucketName = (String) request - .getAttribute(S3Constants.BUCKET_ATTR_KEY); + HttpServletResponse response) { + String bucketName = (String) request + .getAttribute(S3Constants.BUCKET_ATTR_KEY); - SBucketVO bucket = bucketDao.getByName(bucketName); - if (bucket != null) { - String client = UserContext.current().getCanonicalUserId(); - if (!client.equals(bucket.getOwnerCanonicalId())) { - response.setStatus(405); - return; - } - } + SBucketVO bucket = bucketDao.getByName(bucketName); + if (bucket != null) { + String client = UserContext.current().getCanonicalUserId(); + if (!client.equals(bucket.getOwnerCanonicalId())) { + response.setStatus(405); + return; + } + } - try { + try { - String policy = bPolicyDao.getByName(bucketName).getPolicy(); - if (null == policy) { - response.setStatus(204); - } else { - ServiceProvider.getInstance().deleteBucketPolicy(bucketName); - bPolicyDao.deletePolicy(bucketName); - response.setStatus(200); - } - } catch (Exception e) { - logger.error( - "Delete Bucket Policy failed due to " + e.getMessage(), e); - response.setStatus(500); - } + String policy = bPolicyDao.getByName(bucketName).getPolicy(); + if (null == policy) { + response.setStatus(204); + } else { + ServiceProvider.getInstance().deleteBucketPolicy(bucketName); + bPolicyDao.deletePolicy(bucketName); + response.setStatus(200); + } + } catch (Exception e) { + logger.error( + "Delete Bucket Policy failed due to " + e.getMessage(), e); + response.setStatus(500); + } } public void executeGetAllBuckets(HttpServletRequest request, - HttpServletResponse response) throws IOException, - XMLStreamException { - Calendar cal = Calendar.getInstance(); - cal.set(1970, 1, 1); - S3ListAllMyBucketsRequest engineRequest = new S3ListAllMyBucketsRequest(); - engineRequest.setAccessKey(UserContext.current().getAccessKey()); - engineRequest.setRequestTimestamp(cal); - engineRequest.setSignature(""); + HttpServletResponse response) throws IOException, + XMLStreamException { + Calendar cal = Calendar.getInstance(); + cal.set(1970, 1, 1); + S3ListAllMyBucketsRequest engineRequest = new S3ListAllMyBucketsRequest(); + engineRequest.setAccessKey(UserContext.current().getAccessKey()); + engineRequest.setRequestTimestamp(cal); + engineRequest.setSignature(""); - S3ListAllMyBucketsResponse engineResponse = ServiceProvider - .getInstance().getS3Engine().handleRequest(engineRequest); + S3ListAllMyBucketsResponse engineResponse = ServiceProvider + .getInstance().getS3Engine().handleRequest(engineRequest); - // To allow the all buckets list to be serialized via Axiom classes - ListAllMyBucketsResponse allBuckets = S3SerializableServiceImplementation - .toListAllMyBucketsResponse(engineResponse); + // To allow the all buckets list to be serialized via Axiom classes + ListAllMyBucketsResponse allBuckets = S3SerializableServiceImplementation + .toListAllMyBucketsResponse(engineResponse); - OutputStream outputStream = response.getOutputStream(); - response.setStatus(200); - response.setContentType("application/xml"); - // The content-type literally should be "application/xml; charset=UTF-8" - // but any compliant JVM supplies utf-8 by default + OutputStream outputStream = response.getOutputStream(); + response.setStatus(200); + response.setContentType("application/xml"); + // The content-type literally should be "application/xml; charset=UTF-8" + // but any compliant JVM supplies utf-8 by default - // MTOMAwareResultStreamWriter resultWriter = new - // MTOMAwareResultStreamWriter ("ListAllMyBucketsResult", outputStream - // ); - // resultWriter.startWrite(); - // resultWriter.writeout(allBuckets); - // resultWriter.stopWrite(); - StringBuffer xml = new StringBuffer(); - xml.append(""); - xml.append(""); - xml.append(""); - xml.append(engineResponse.getOwner().getID()).append(""); - xml.append("") - .append(engineResponse.getOwner().getDisplayName()) - .append(""); - xml.append("").append(""); - SimpleDateFormat sdf = new SimpleDateFormat( - "yyyy-MM-dd'T'HH:mm:ss.SSSZ"); - for (S3ListAllMyBucketsEntry entry : engineResponse.getBuckets()) { - xml.append("").append("").append(entry.getName()) - .append(""); - xml.append("") - .append(sdf.format(entry.getCreationDate().getTime())) - .append(""); - xml.append(""); - } - xml.append("").append(""); - response.setStatus(200); - response.setContentType("text/xml; charset=UTF-8"); - S3RestServlet.endResponse(response, xml.toString()); - - } + // MTOMAwareResultStreamWriter resultWriter = new + // MTOMAwareResultStreamWriter ("ListAllMyBucketsResult", outputStream + // ); + // resultWriter.startWrite(); + // resultWriter.writeout(allBuckets); + // resultWriter.stopWrite(); + StringBuffer xml = new StringBuffer(); + xml.append(""); + xml.append(""); + xml.append(""); + xml.append(engineResponse.getOwner().getID()).append(""); + xml.append("") + .append(engineResponse.getOwner().getDisplayName()) + .append(""); + xml.append("").append(""); + SimpleDateFormat sdf = new SimpleDateFormat( + "yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + for (S3ListAllMyBucketsEntry entry : engineResponse.getBuckets()) { + xml.append("").append("").append(entry.getName()) + .append(""); + xml.append("") + .append(sdf.format(entry.getCreationDate().getTime())) + .append(""); + xml.append(""); + } + xml.append("").append(""); + response.setStatus(200); + response.setContentType("text/xml; charset=UTF-8"); + S3RestServlet.endResponse(response, xml.toString()); - public void executeGetBucket(HttpServletRequest request, HttpServletResponse response) - throws IOException, XMLStreamException - { - S3ListBucketRequest engineRequest = new S3ListBucketRequest(); - engineRequest.setBucketName((String) request - .getAttribute(S3Constants.BUCKET_ATTR_KEY)); - engineRequest.setDelimiter(request.getParameter("delimiter")); - engineRequest.setMarker(request.getParameter("marker")); - engineRequest.setPrefix(request.getParameter("prefix")); + } - int maxKeys = Converter.toInt(request.getParameter("max-keys"), 1000); - engineRequest.setMaxKeys(maxKeys); - try { - S3ListBucketResponse engineResponse = ServiceProvider.getInstance() - .getS3Engine().listBucketContents(engineRequest, false); + public void executeGetBucket(HttpServletRequest request, HttpServletResponse response) + throws IOException, XMLStreamException + { + S3ListBucketRequest engineRequest = new S3ListBucketRequest(); + engineRequest.setBucketName((String) request + .getAttribute(S3Constants.BUCKET_ATTR_KEY)); + engineRequest.setDelimiter(request.getParameter("delimiter")); + engineRequest.setMarker(request.getParameter("marker")); + engineRequest.setPrefix(request.getParameter("prefix")); - // To allow the all list buckets result to be serialized via Axiom - // classes - ListBucketResponse oneBucket = S3SerializableServiceImplementation - .toListBucketResponse(engineResponse); + int maxKeys = Converter.toInt(request.getParameter("max-keys"), 1000); + engineRequest.setMaxKeys(maxKeys); + try { + S3ListBucketResponse engineResponse = ServiceProvider.getInstance() + .getS3Engine().listBucketContents(engineRequest, false); - OutputStream outputStream = response.getOutputStream(); - response.setStatus(200); - response.setContentType("application/xml"); - // The content-type literally should be - // "application/xml; charset=UTF-8" - // but any compliant JVM supplies utf-8 by default; + // To allow the all list buckets result to be serialized via Axiom + // classes + ListBucketResponse oneBucket = S3SerializableServiceImplementation + .toListBucketResponse(engineResponse); - MTOMAwareResultStreamWriter resultWriter = new MTOMAwareResultStreamWriter( - "ListBucketResult", outputStream); - resultWriter.startWrite(); - resultWriter.writeout(oneBucket); - resultWriter.stopWrite(); - } catch (NoSuchObjectException nsoe) { - response.setStatus(404); - response.setContentType("application/xml"); + OutputStream outputStream = response.getOutputStream(); + response.setStatus(200); + response.setContentType("application/xml"); + // The content-type literally should be + // "application/xml; charset=UTF-8" + // but any compliant JVM supplies utf-8 by default; - StringBuffer xmlError = new StringBuffer(); - xmlError.append("") - .append("NoSuchBucketThe specified bucket does not exist") - .append("") - .append((String) request - .getAttribute(S3Constants.BUCKET_ATTR_KEY)) - .append("") - .append("1DEADBEEF9") // TODO - .append("abCdeFgHiJ1k2LmN3op4q56r7st89") // TODO - .append(""); - S3RestServlet.endResponse(response, xmlError.toString()); + MTOMAwareResultStreamWriter resultWriter = new MTOMAwareResultStreamWriter( + "ListBucketResult", outputStream); + resultWriter.startWrite(); + resultWriter.writeout(oneBucket); + resultWriter.stopWrite(); + } catch (NoSuchObjectException nsoe) { + response.setStatus(404); + response.setContentType("application/xml"); - } + StringBuffer xmlError = new StringBuffer(); + xmlError.append("") + .append("NoSuchBucketThe specified bucket does not exist") + .append("") + .append((String) request + .getAttribute(S3Constants.BUCKET_ATTR_KEY)) + .append("") + .append("1DEADBEEF9") // TODO + .append("abCdeFgHiJ1k2LmN3op4q56r7st89") // TODO + .append(""); + S3RestServlet.endResponse(response, xmlError.toString()); - } - - public void executeGetBucketAcl(HttpServletRequest request, HttpServletResponse response) - throws IOException, XMLStreamException - { - S3GetBucketAccessControlPolicyRequest engineRequest = new S3GetBucketAccessControlPolicyRequest(); - Calendar cal = Calendar.getInstance(); - cal.set( 1970, 1, 1 ); - engineRequest.setAccessKey(UserContext.current().getAccessKey()); - engineRequest.setRequestTimestamp( cal ); - engineRequest.setSignature( "" ); // TODO - Consider providing signature in a future release which allows additional user description - engineRequest.setBucketName((String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY)); + } - S3AccessControlPolicy engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); - - // To allow the bucket acl policy result to be serialized via Axiom classes - GetBucketAccessControlPolicyResponse onePolicy = S3SerializableServiceImplementation.toGetBucketAccessControlPolicyResponse( engineResponse ); + } - OutputStream outputStream = response.getOutputStream(); - response.setStatus(200); - response.setContentType("application/xml"); - // The content-type literally should be "application/xml; charset=UTF-8" - // but any compliant JVM supplies utf-8 by default; - - MTOMAwareResultStreamWriter resultWriter = new MTOMAwareResultStreamWriter ("GetBucketAccessControlPolicyResult", outputStream ); - resultWriter.startWrite(); - resultWriter.writeout(onePolicy); - resultWriter.stopWrite(); + public void executeGetBucketAcl(HttpServletRequest request, HttpServletResponse response) + throws IOException, XMLStreamException + { + S3GetBucketAccessControlPolicyRequest engineRequest = new S3GetBucketAccessControlPolicyRequest(); + Calendar cal = Calendar.getInstance(); + cal.set( 1970, 1, 1 ); + engineRequest.setAccessKey(UserContext.current().getAccessKey()); + engineRequest.setRequestTimestamp( cal ); + engineRequest.setSignature( "" ); // TODO - Consider providing signature in a future release which allows additional user description + engineRequest.setBucketName((String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY)); - - } - - public void executeGetBucketVersioning(HttpServletRequest request, HttpServletResponse response) throws IOException - { - // [A] Does the bucket exist? - String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String versioningStatus = null; - - if (null == bucketName) { - logger.error( "executeGetBucketVersioning - no bucket name given" ); - response.setStatus( 400 ); - return; - } - - SBucketVO sbucket = bucketDao.getByName( bucketName ); - if (sbucket == null) { - response.setStatus( 404 ); - return; - } - - // [B] The owner may want to restrict the IP address at which this can be performed - String client = UserContext.current().getCanonicalUserId(); - if (!client.equals( sbucket.getOwnerCanonicalId())) - throw new PermissionDeniedException( "Access Denied - only the owner can read bucket versioning" ); + S3AccessControlPolicy engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); - S3PolicyContext context = new S3PolicyContext( PolicyActions.GetBucketVersioning, bucketName ); - if (PolicyAccess.DENY == S3Engine.verifyPolicy( context )) { - response.setStatus(403); - return; - } + // To allow the bucket acl policy result to be serialized via Axiom classes + GetBucketAccessControlPolicyResponse onePolicy = S3SerializableServiceImplementation.toGetBucketAccessControlPolicyResponse( engineResponse ); + + OutputStream outputStream = response.getOutputStream(); + response.setStatus(200); + response.setContentType("application/xml"); + // The content-type literally should be "application/xml; charset=UTF-8" + // but any compliant JVM supplies utf-8 by default; + + MTOMAwareResultStreamWriter resultWriter = new MTOMAwareResultStreamWriter ("GetBucketAccessControlPolicyResult", outputStream ); + resultWriter.startWrite(); + resultWriter.writeout(onePolicy); + resultWriter.stopWrite(); - // [C] - switch( sbucket.getVersioningStatus()) { - default: - case 0: versioningStatus = ""; break; - case 1: versioningStatus = "Enabled"; break; - case 2: versioningStatus = "Suspended"; break; - } + } - StringBuffer xml = new StringBuffer(); + public void executeGetBucketVersioning(HttpServletRequest request, HttpServletResponse response) throws IOException + { + // [A] Does the bucket exist? + String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String versioningStatus = null; + + if (null == bucketName) { + logger.error( "executeGetBucketVersioning - no bucket name given" ); + response.setStatus( 400 ); + return; + } + + SBucketVO sbucket = bucketDao.getByName( bucketName ); + if (sbucket == null) { + response.setStatus( 404 ); + return; + } + + // [B] The owner may want to restrict the IP address at which this can be performed + String client = UserContext.current().getCanonicalUserId(); + if (!client.equals( sbucket.getOwnerCanonicalId())) + throw new PermissionDeniedException( "Access Denied - only the owner can read bucket versioning" ); + + S3PolicyContext context = new S3PolicyContext( PolicyActions.GetBucketVersioning, bucketName ); + if (PolicyAccess.DENY == S3Engine.verifyPolicy( context )) { + response.setStatus(403); + return; + } + + + // [C] + switch( sbucket.getVersioningStatus()) { + default: + case 0: versioningStatus = ""; break; + case 1: versioningStatus = "Enabled"; break; + case 2: versioningStatus = "Suspended"; break; + } + + StringBuffer xml = new StringBuffer(); xml.append( "" ); xml.append( "" ); if (0 < versioningStatus.length()) xml.append( "" ).append( versioningStatus ).append( "" ); xml.append( "" ); - - response.setStatus(200); - response.setContentType("text/xml; charset=UTF-8"); - S3RestServlet.endResponse(response, xml.toString()); - } - - public void executeGetBucketObjectVersions(HttpServletRequest request, HttpServletResponse response) throws IOException - { - S3ListBucketRequest engineRequest = new S3ListBucketRequest(); - String keyMarker = request.getParameter("key-marker"); - String versionIdMarker = request.getParameter("version-id-marker"); - - engineRequest.setBucketName((String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY)); - engineRequest.setDelimiter(request.getParameter("delimiter")); - engineRequest.setMarker( keyMarker ); - engineRequest.setPrefix(request.getParameter("prefix")); - engineRequest.setVersionIdMarker( versionIdMarker ); - - int maxKeys = Converter.toInt(request.getParameter("max-keys"), 1000); - engineRequest.setMaxKeys(maxKeys); - S3ListBucketResponse engineResponse = ServiceProvider.getInstance().getS3Engine().listBucketContents( engineRequest, true ); - - // -> the SOAP version produces different XML - StringBuffer xml = new StringBuffer(); + + response.setStatus(200); + response.setContentType("text/xml; charset=UTF-8"); + S3RestServlet.endResponse(response, xml.toString()); + } + + public void executeGetBucketObjectVersions(HttpServletRequest request, HttpServletResponse response) throws IOException + { + S3ListBucketRequest engineRequest = new S3ListBucketRequest(); + String keyMarker = request.getParameter("key-marker"); + String versionIdMarker = request.getParameter("version-id-marker"); + + engineRequest.setBucketName((String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY)); + engineRequest.setDelimiter(request.getParameter("delimiter")); + engineRequest.setMarker( keyMarker ); + engineRequest.setPrefix(request.getParameter("prefix")); + engineRequest.setVersionIdMarker( versionIdMarker ); + + int maxKeys = Converter.toInt(request.getParameter("max-keys"), 1000); + engineRequest.setMaxKeys(maxKeys); + S3ListBucketResponse engineResponse = ServiceProvider.getInstance().getS3Engine().listBucketContents( engineRequest, true ); + + // -> the SOAP version produces different XML + StringBuffer xml = new StringBuffer(); xml.append( "" ); xml.append( "" ); xml.append( "" ).append( engineResponse.getBucketName()).append( "" ); - + if ( null == keyMarker ) - xml.append( "" ); + xml.append( "" ); else xml.append( "" ).append( keyMarker ).append( "" ); + xml.append( "" ); else xml.append( "" ).append( keyMarker ).append( "" ).append( engineResponse.getMaxKeys()).append( "" ); xml.append( "" ).append( engineResponse.isTruncated()).append( "" ); - + S3ListBucketObjectEntry[] versions = engineResponse.getContents(); for( int i=0; null != versions && i < versions.length; i++ ) { - S3CanonicalUser owner = versions[i].getOwner(); - boolean isDeletionMarker = versions[i].getIsDeletionMarker(); - String displayName = owner.getDisplayName(); - String id = owner.getID(); - - if ( isDeletionMarker ) - { - xml.append( "" ); - xml.append( "" ).append( versions[i].getKey()).append( "" ); - xml.append( "" ).append( versions[i].getVersion()).append( "" ); - xml.append( "" ).append( versions[i].getIsLatest()).append( "" ); - xml.append( "" ).append( DatatypeConverter.printDateTime( versions[i].getLastModified())).append( "" ); - } - else - { xml.append( "" ); - xml.append( "" ).append( versions[i].getKey()).append( "" ); - xml.append( "" ).append( versions[i].getVersion()).append( "" ); - xml.append( "" ).append( versions[i].getIsLatest()).append( "" ); - xml.append( "" ).append( DatatypeConverter.printDateTime( versions[i].getLastModified())).append( "" ); - xml.append( "" ).append( versions[i].getETag()).append( "" ); - xml.append( "" ).append( versions[i].getSize()).append( "" ); - xml.append( "" ).append( versions[i].getStorageClass()).append( "" ); - } - - xml.append( "" ); - xml.append( "" ).append( id ).append( "" ); - if ( null == displayName ) - xml.append( "" ); - else xml.append( "" ).append( owner.getDisplayName()).append( "" ); - xml.append( "" ); - - if ( isDeletionMarker ) - xml.append( "" ); - else xml.append( "" ); + S3CanonicalUser owner = versions[i].getOwner(); + boolean isDeletionMarker = versions[i].getIsDeletionMarker(); + String displayName = owner.getDisplayName(); + String id = owner.getID(); + + if ( isDeletionMarker ) + { + xml.append( "" ); + xml.append( "" ).append( versions[i].getKey()).append( "" ); + xml.append( "" ).append( versions[i].getVersion()).append( "" ); + xml.append( "" ).append( versions[i].getIsLatest()).append( "" ); + xml.append( "" ).append( DatatypeConverter.printDateTime( versions[i].getLastModified())).append( "" ); + } + else + { xml.append( "" ); + xml.append( "" ).append( versions[i].getKey()).append( "" ); + xml.append( "" ).append( versions[i].getVersion()).append( "" ); + xml.append( "" ).append( versions[i].getIsLatest()).append( "" ); + xml.append( "" ).append( DatatypeConverter.printDateTime( versions[i].getLastModified())).append( "" ); + xml.append( "" ).append( versions[i].getETag()).append( "" ); + xml.append( "" ).append( versions[i].getSize()).append( "" ); + xml.append( "" ).append( versions[i].getStorageClass()).append( "" ); + } + + xml.append( "" ); + xml.append( "" ).append( id ).append( "" ); + if ( null == displayName ) + xml.append( "" ); + else xml.append( "" ).append( owner.getDisplayName()).append( "" ); + xml.append( "" ); + + if ( isDeletionMarker ) + xml.append( "" ); + else xml.append( "" ); } xml.append( "" ); - - response.setStatus(200); - response.setContentType("text/xml; charset=UTF-8"); - S3RestServlet.endResponse(response, xml.toString()); - } - - public void executeGetBucketLogging(HttpServletRequest request, HttpServletResponse response) throws IOException { - // TODO -- Review this in future. Currently this is a beta feature of S3 - response.setStatus(405); - } - - public void executeGetBucketLocation(HttpServletRequest request, HttpServletResponse response) throws IOException { - // TODO - This is a fakery! We don't actually store location in backend - StringBuffer xml = new StringBuffer(); - xml.append( "" ); - xml.append( "" ); - // This is the real fakery - xml.append( "us-west-2" ); - xml.append( "" ); - response.setStatus(200); - response.setContentType("text/xml; charset=UTF-8"); - S3RestServlet.endResponse(response, xml.toString()); - } - public void executeGetBucketWebsite(HttpServletRequest request, HttpServletResponse response) throws IOException { - response.setStatus(405); - } + response.setStatus(200); + response.setContentType("text/xml; charset=UTF-8"); + S3RestServlet.endResponse(response, xml.toString()); + } - public void executeDeleteBucketWebsite(HttpServletRequest request, HttpServletResponse response) throws IOException { - response.setStatus(405); - } + public void executeGetBucketLogging(HttpServletRequest request, HttpServletResponse response) throws IOException { + // TODO -- Review this in future. Currently this is a beta feature of S3 + response.setStatus(405); + } - public void executePutBucket(HttpServletRequest request, HttpServletResponse response) throws IOException - { - int contentLength = request.getContentLength(); - Object objectInContent = null; - - if(contentLength > 0) - { - InputStream is = null; - try { - is = request.getInputStream(); - String xml = StringHelper.stringFromStream(is); - Class.forName("com.cloud.bridge.service.core.s3.S3CreateBucketConfiguration"); - XSerializer serializer = new XSerializer(new XSerializerXmlAdapter()); - objectInContent = serializer.serializeFrom(xml); - if(objectInContent != null && !(objectInContent instanceof S3CreateBucketConfiguration)) { - throw new InvalidRequestContentException("Invalid request content in create-bucket: " + xml); - } - is.close(); - - } catch (IOException e) { - logger.error("Unable to read request data due to " + e.getMessage(), e); - throw new NetworkIOException(e); - - } catch (ClassNotFoundException e) { - logger.error("In a normal world this should never never happen:" + e.getMessage(), e); - throw new RuntimeException("A required class was not found in the classpath:" + e.getMessage()); - } - finally { - if(is != null) is.close(); - } - } - - S3CreateBucketRequest engineRequest = new S3CreateBucketRequest(); - engineRequest.setBucketName((String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY)); - engineRequest.setConfig((S3CreateBucketConfiguration)objectInContent); - try { - S3CreateBucketResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); - response.addHeader("Location", "/" + engineResponse.getBucketName()); - response.setContentLength(0); - response.setStatus(200); - response.flushBuffer(); - } catch (ObjectAlreadyExistsException oaee) { - response.setStatus(409); - String xml = " OperationAbortedA conflicting conditional operation is currently in progress against this resource. Please try again.."; - response.setContentType("text/xml; charset=UTF-8"); - S3RestServlet.endResponse(response, xml.toString()); - } - } - - public void executePutBucketAcl(HttpServletRequest request, HttpServletResponse response) throws IOException - { - // [A] Determine that there is an applicable bucket which might have an ACL set + public void executeGetBucketLocation(HttpServletRequest request, HttpServletResponse response) throws IOException { + // TODO - This is a fakery! We don't actually store location in backend + StringBuffer xml = new StringBuffer(); + xml.append( "" ); + xml.append( "" ); + // This is the real fakery + xml.append( "us-west-2" ); + xml.append( "" ); + response.setStatus(200); + response.setContentType("text/xml; charset=UTF-8"); + S3RestServlet.endResponse(response, xml.toString()); + } - String bucketName = (String) request - .getAttribute(S3Constants.BUCKET_ATTR_KEY); - SBucketVO bucket = bucketDao.getByName(bucketName); - String owner = null; - if (null != bucket) - owner = bucket.getOwnerCanonicalId(); - if (null == owner) { - logger.error("ACL update failed since " + bucketName - + " does not exist"); - throw new IOException("ACL update failed"); - } + public void executeGetBucketWebsite(HttpServletRequest request, HttpServletResponse response) throws IOException { + response.setStatus(405); + } - // [B] Obtain the grant request which applies to the acl request string. - // This latter is supplied as the value of the x-amz-acl header. + public void executeDeleteBucketWebsite(HttpServletRequest request, HttpServletResponse response) throws IOException { + response.setStatus(405); + } - S3SetBucketAccessControlPolicyRequest engineRequest = new S3SetBucketAccessControlPolicyRequest(); - S3Grant grantRequest = new S3Grant(); - S3AccessControlList aclRequest = new S3AccessControlList(); + public void executePutBucket(HttpServletRequest request, HttpServletResponse response) throws IOException + { + int contentLength = request.getContentLength(); + Object objectInContent = null; - String aclRequestString = request.getHeader("x-amz-acl"); - OrderedPair accessControlsForBucketOwner = SAclVO.getCannedAccessControls(aclRequestString, "SBucket"); - grantRequest.setPermission(accessControlsForBucketOwner.getFirst()); - grantRequest.setGrantee(accessControlsForBucketOwner.getSecond()); - grantRequest.setCanonicalUserID(owner); - aclRequest.addGrant(grantRequest); - engineRequest.setAcl(aclRequest); - engineRequest.setBucketName(bucketName); + if(contentLength > 0) + { + InputStream is = null; + try { + is = request.getInputStream(); + String xml = StringHelper.stringFromStream(is); + Class.forName("com.cloud.bridge.service.core.s3.S3CreateBucketConfiguration"); + XSerializer serializer = new XSerializer(new XSerializerXmlAdapter()); + objectInContent = serializer.serializeFrom(xml); + if(objectInContent != null && !(objectInContent instanceof S3CreateBucketConfiguration)) { + throw new InvalidRequestContentException("Invalid request content in create-bucket: " + xml); + } + is.close(); - // [C] Allow an S3Engine to handle the - // S3SetBucketAccessControlPolicyRequest - S3Response engineResponse = ServiceProvider.getInstance().getS3Engine() - .handleRequest(engineRequest); - response.setStatus(engineResponse.getResultCode()); + } catch (IOException e) { + logger.error("Unable to read request data due to " + e.getMessage(), e); + throw new NetworkIOException(e); - } - - public void executePutBucketVersioning(HttpServletRequest request, HttpServletResponse response) throws IOException - { - String bucketName = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String versioningStatus = null; - Node item = null; + } catch (ClassNotFoundException e) { + logger.error("In a normal world this should never never happen:" + e.getMessage(), e); + throw new RuntimeException("A required class was not found in the classpath:" + e.getMessage()); + } + finally { + if(is != null) is.close(); + } + } - if (null == bucketName) { - logger.error("executePutBucketVersioning - no bucket name given"); - response.setStatus(400); - return; - } + S3CreateBucketRequest engineRequest = new S3CreateBucketRequest(); + engineRequest.setBucketName((String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY)); + engineRequest.setConfig((S3CreateBucketConfiguration)objectInContent); + try { + S3CreateBucketResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); + response.addHeader("Location", "/" + engineResponse.getBucketName()); + response.setContentLength(0); + response.setStatus(200); + response.flushBuffer(); + } catch (ObjectAlreadyExistsException oaee) { + response.setStatus(409); + String xml = " OperationAbortedA conflicting conditional operation is currently in progress against this resource. Please try again.."; + response.setContentType("text/xml; charset=UTF-8"); + S3RestServlet.endResponse(response, xml.toString()); + } + } - // -> is the XML as defined? - try { - DocumentBuilder db = dbf.newDocumentBuilder(); - Document restXML = db.parse(request.getInputStream()); - NodeList match = S3RestServlet.getElement(restXML, - "http://s3.amazonaws.com/doc/2006-03-01/", "Status"); - if (0 < match.getLength()) { - item = match.item(0); - versioningStatus = new String(item.getFirstChild() - .getNodeValue()); - } else { - logger.error("executePutBucketVersioning - cannot find Status tag in XML body"); - response.setStatus(400); - return; - } - } catch (Exception e) { - logger.error( - "executePutBucketVersioning - failed to parse XML due to " - + e.getMessage(), e); - response.setStatus(400); - return; - } + public void executePutBucketAcl(HttpServletRequest request, HttpServletResponse response) throws IOException + { + // [A] Determine that there is an applicable bucket which might have an ACL set - try { - // Irrespective of what the ACLs say only the owner can turn on - // versioning on a bucket. - // The bucket owner may want to restrict the IP address from which - // this can occur. - - SBucketVO sbucket = bucketDao.getByName(bucketName); + String bucketName = (String) request + .getAttribute(S3Constants.BUCKET_ATTR_KEY); + SBucketVO bucket = bucketDao.getByName(bucketName); + String owner = null; + if (null != bucket) + owner = bucket.getOwnerCanonicalId(); + if (null == owner) { + logger.error("ACL update failed since " + bucketName + + " does not exist"); + throw new IOException("ACL update failed"); + } - String client = UserContext.current().getCanonicalUserId(); - if (!client.equals(sbucket.getOwnerCanonicalId())) - throw new PermissionDeniedException( - "Access Denied - only the owner can turn on versioing on a bucket"); + // [B] Obtain the grant request which applies to the acl request string. + // This latter is supplied as the value of the x-amz-acl header. - S3PolicyContext context = new S3PolicyContext( - PolicyActions.PutBucketVersioning, bucketName); - if (PolicyAccess.DENY == S3Engine.verifyPolicy(context)) { - response.setStatus(403); - return; - } + S3SetBucketAccessControlPolicyRequest engineRequest = new S3SetBucketAccessControlPolicyRequest(); + S3Grant grantRequest = new S3Grant(); + S3AccessControlList aclRequest = new S3AccessControlList(); - if (versioningStatus.equalsIgnoreCase("Enabled")) - sbucket.setVersioningStatus(1); - else if (versioningStatus.equalsIgnoreCase("Suspended")) - sbucket.setVersioningStatus(2); - else { - logger.error("executePutBucketVersioning - unknown state: [" - + versioningStatus + "]"); - response.setStatus(400); - return; - } - bucketDao.update(sbucket.getId(), sbucket); + String aclRequestString = request.getHeader("x-amz-acl"); + OrderedPair accessControlsForBucketOwner = SAclVO.getCannedAccessControls(aclRequestString, "SBucket"); + grantRequest.setPermission(accessControlsForBucketOwner.getFirst()); + grantRequest.setGrantee(accessControlsForBucketOwner.getSecond()); + grantRequest.setCanonicalUserID(owner); + aclRequest.addGrant(grantRequest); + engineRequest.setAcl(aclRequest); + engineRequest.setBucketName(bucketName); - } catch (PermissionDeniedException e) { - logger.error( - "executePutBucketVersioning - failed due to " - + e.getMessage(), e); - throw e; + // [C] Allow an S3Engine to handle the + // S3SetBucketAccessControlPolicyRequest + S3Response engineResponse = ServiceProvider.getInstance().getS3Engine() + .handleRequest(engineRequest); + response.setStatus(engineResponse.getResultCode()); - } catch (Exception e) { - logger.error( - "executePutBucketVersioning - failed due to " - + e.getMessage(), e); - response.setStatus(500); - return; - } - response.setStatus(200); - } - - public void executePutBucketLogging(HttpServletRequest request, HttpServletResponse response) throws IOException { - // TODO -- Review this in future. Currently this is a S3 beta feature - response.setStatus(501); - } - - public void executePutBucketWebsite(HttpServletRequest request, HttpServletResponse response) throws IOException { - // TODO -- LoPri - Undertake checks on Put Bucket Website - // Tested using configuration \nAllowOverride FileInfo AuthConfig Limit... in httpd.conf - // Need some way of using AllowOverride to allow use of .htaccess and then pushing .httaccess file to bucket subdirectory of mount point - // Currently has noop effect in the sense that a running apachectl process sees the directory contents without further action - response.setStatus(200); - } + } - public void executeDeleteBucket(HttpServletRequest request, HttpServletResponse response) throws IOException - { - S3DeleteBucketRequest engineRequest = new S3DeleteBucketRequest(); - engineRequest.setBucketName((String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY)); - S3Response engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); - response.setStatus(engineResponse.getResultCode()); - response.flushBuffer(); - } - - /** - * Multipart upload is a complex operation with all the options defined by Amazon. Part of the functionality is - * provided by the query done against the database. The CommonPrefixes functionality is done the same way - * as done in the listBucketContents function (i.e., by iterating though the list to decide which output - * element each key is placed). - * - * @param request - * @param response - * @throws IOException - */ - public void executeListMultipartUploads(HttpServletRequest request, HttpServletResponse response) throws IOException - { - // [A] Obtain parameters and do basic bucket verification - String bucketName = (String) request - .getAttribute(S3Constants.BUCKET_ATTR_KEY); - String delimiter = request.getParameter("delimiter"); - String keyMarker = request.getParameter("key-marker"); - String prefix = request.getParameter("prefix"); - int maxUploads = 1000; - int nextUploadId = 0; - String nextKey = null; - boolean isTruncated = false; - S3MultipartUpload[] uploads = null; - S3MultipartUpload onePart = null; - String temp = request.getParameter("max-uploads"); - if (null != temp) { - maxUploads = Integer.parseInt(temp); - if (maxUploads > 1000 || maxUploads < 0) - maxUploads = 1000; - } + public void executePutBucketVersioning(HttpServletRequest request, HttpServletResponse response) throws IOException + { + String bucketName = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String versioningStatus = null; + Node item = null; - // -> upload-id-marker is ignored unless key-marker is also specified - String uploadIdMarker = request.getParameter("upload-id-marker"); - if (null == keyMarker) - uploadIdMarker = null; + if (null == bucketName) { + logger.error("executePutBucketVersioning - no bucket name given"); + response.setStatus(400); + return; + } - // -> does the bucket exist, we may need it to verify access permissions - SBucketVO bucket = bucketDao.getByName(bucketName); - if (bucket == null) { - logger.error("listMultipartUpload failed since " + bucketName - + " does not exist"); - response.setStatus(404); - return; - } + // -> is the XML as defined? + try { + DocumentBuilder db = dbf.newDocumentBuilder(); + Document restXML = db.parse(request.getInputStream()); + NodeList match = S3RestServlet.getElement(restXML, + "http://s3.amazonaws.com/doc/2006-03-01/", "Status"); + if (0 < match.getLength()) { + item = match.item(0); + versioningStatus = new String(item.getFirstChild() + .getNodeValue()); + } else { + logger.error("executePutBucketVersioning - cannot find Status tag in XML body"); + response.setStatus(400); + return; + } + } catch (Exception e) { + logger.error( + "executePutBucketVersioning - failed to parse XML due to " + + e.getMessage(), e); + response.setStatus(400); + return; + } - S3PolicyContext context = new S3PolicyContext( - PolicyActions.ListBucketMultipartUploads, bucketName); - context.setEvalParam(ConditionKeys.Prefix, prefix); - context.setEvalParam(ConditionKeys.Delimiter, delimiter); - S3Engine.verifyAccess(context, "SBucket", bucket.getId(), - SAcl.PERMISSION_READ); + try { + // Irrespective of what the ACLs say only the owner can turn on + // versioning on a bucket. + // The bucket owner may want to restrict the IP address from which + // this can occur. - // [B] Query the multipart table to get the list of current uploads - try { - MultipartLoadDao uploadDao = new MultipartLoadDao(); - OrderedPair result = uploadDao - .getInitiatedUploads(bucketName, maxUploads, prefix, - keyMarker, uploadIdMarker); - uploads = result.getFirst(); - isTruncated = result.getSecond().booleanValue(); - } catch (Exception e) { - logger.error( - "List Multipart Uploads failed due to " + e.getMessage(), e); - response.setStatus(500); - } + SBucketVO sbucket = bucketDao.getByName(bucketName); - StringBuffer xml = new StringBuffer(); - xml.append(""); - xml.append(""); - xml.append("").append(bucketName).append(""); - xml.append("").append((null == keyMarker ? "" : keyMarker)) - .append(""); - xml.append("") - .append((null == uploadIdMarker ? "" : uploadIdMarker)) - .append(""); + String client = UserContext.current().getCanonicalUserId(); + if (!client.equals(sbucket.getOwnerCanonicalId())) + throw new PermissionDeniedException( + "Access Denied - only the owner can turn on versioing on a bucket"); - // [C] Construct the contents of the element - StringBuffer partsList = new StringBuffer(); - for (int i = 0; i < uploads.length; i++) { - onePart = uploads[i]; - if (null == onePart) - break; + S3PolicyContext context = new S3PolicyContext( + PolicyActions.PutBucketVersioning, bucketName); + if (PolicyAccess.DENY == S3Engine.verifyPolicy(context)) { + response.setStatus(403); + return; + } - if (delimiter != null && !delimiter.isEmpty()) { - // -> is this available only in the CommonPrefixes element? - if (StringHelper.substringInBetween(onePart.getKey(), prefix, - delimiter) != null) - continue; - } + if (versioningStatus.equalsIgnoreCase("Enabled")) + sbucket.setVersioningStatus(1); + else if (versioningStatus.equalsIgnoreCase("Suspended")) + sbucket.setVersioningStatus(2); + else { + logger.error("executePutBucketVersioning - unknown state: [" + + versioningStatus + "]"); + response.setStatus(400); + return; + } + bucketDao.update(sbucket.getId(), sbucket); - nextKey = onePart.getKey(); - nextUploadId = onePart.getId(); - partsList.append(""); - partsList.append("").append(nextKey).append(""); - partsList.append("").append(nextUploadId) - .append(""); - partsList.append(""); - partsList.append("").append(onePart.getAccessKey()) - .append(""); - partsList.append(""); - partsList.append(""); - partsList.append(""); - partsList.append("").append(onePart.getAccessKey()) - .append(""); - partsList.append(""); - partsList.append(""); - partsList.append("STANDARD"); - partsList - .append("") - .append(DatatypeConverter.printDateTime(onePart - .getLastModified())).append(""); - partsList.append(""); - } + } catch (PermissionDeniedException e) { + logger.error( + "executePutBucketVersioning - failed due to " + + e.getMessage(), e); + throw e; - // [D] Construct the contents of the elements (if any) - for (int i = 0; i < uploads.length; i++) { - onePart = uploads[i]; - if (null == onePart) - break; + } catch (Exception e) { + logger.error( + "executePutBucketVersioning - failed due to " + + e.getMessage(), e); + response.setStatus(500); + return; + } + response.setStatus(200); + } - if (delimiter != null && !delimiter.isEmpty()) { - String subName = StringHelper.substringInBetween( - onePart.getKey(), prefix, delimiter); - if (subName != null) { - partsList.append(""); - partsList.append(""); - if (prefix != null && prefix.length() > 0) - partsList.append(prefix + delimiter + subName); - else - partsList.append(subName); - partsList.append(""); - partsList.append(""); - } - } - } + public void executePutBucketLogging(HttpServletRequest request, HttpServletResponse response) throws IOException { + // TODO -- Review this in future. Currently this is a S3 beta feature + response.setStatus(501); + } - // [D] Finish off the response - xml.append("").append((null == nextKey ? "" : nextKey)) - .append(""); - xml.append("") - .append((0 == nextUploadId ? "" : nextUploadId)) - .append(""); - xml.append("").append(maxUploads).append(""); - xml.append("").append(isTruncated) - .append(""); + public void executePutBucketWebsite(HttpServletRequest request, HttpServletResponse response) throws IOException { + // TODO -- LoPri - Undertake checks on Put Bucket Website + // Tested using configuration \nAllowOverride FileInfo AuthConfig Limit... in httpd.conf + // Need some way of using AllowOverride to allow use of .htaccess and then pushing .httaccess file to bucket subdirectory of mount point + // Currently has noop effect in the sense that a running apachectl process sees the directory contents without further action + response.setStatus(200); + } - xml.append(partsList.toString()); - xml.append(""); + public void executeDeleteBucket(HttpServletRequest request, HttpServletResponse response) throws IOException + { + S3DeleteBucketRequest engineRequest = new S3DeleteBucketRequest(); + engineRequest.setBucketName((String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY)); + S3Response engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); + response.setStatus(engineResponse.getResultCode()); + response.flushBuffer(); + } - response.setStatus(200); - response.setContentType("text/xml; charset=UTF-8"); - S3RestServlet.endResponse(response, xml.toString()); - } - - private String streamToString( InputStream is ) throws IOException - { - int n = 0; - - if ( null != is ) - { - Writer writer = new StringWriter(); - char[] buffer = new char[1024]; - try { - Reader reader = new BufferedReader( new InputStreamReader(is, "UTF-8")); - while ((n = reader.read(buffer)) != -1) writer.write(buffer, 0, n); - } - finally { - is.close(); - } - return writer.toString(); - } - else return null; + /** + * Multipart upload is a complex operation with all the options defined by Amazon. Part of the functionality is + * provided by the query done against the database. The CommonPrefixes functionality is done the same way + * as done in the listBucketContents function (i.e., by iterating though the list to decide which output + * element each key is placed). + * + * @param request + * @param response + * @throws IOException + */ + public void executeListMultipartUploads(HttpServletRequest request, HttpServletResponse response) throws IOException + { + // [A] Obtain parameters and do basic bucket verification + String bucketName = (String) request + .getAttribute(S3Constants.BUCKET_ATTR_KEY); + String delimiter = request.getParameter("delimiter"); + String keyMarker = request.getParameter("key-marker"); + String prefix = request.getParameter("prefix"); + int maxUploads = 1000; + int nextUploadId = 0; + String nextKey = null; + boolean isTruncated = false; + S3MultipartUpload[] uploads = null; + S3MultipartUpload onePart = null; + String temp = request.getParameter("max-uploads"); + if (null != temp) { + maxUploads = Integer.parseInt(temp); + if (maxUploads > 1000 || maxUploads < 0) + maxUploads = 1000; + } + + // -> upload-id-marker is ignored unless key-marker is also specified + String uploadIdMarker = request.getParameter("upload-id-marker"); + if (null == keyMarker) + uploadIdMarker = null; + + // -> does the bucket exist, we may need it to verify access permissions + SBucketVO bucket = bucketDao.getByName(bucketName); + if (bucket == null) { + logger.error("listMultipartUpload failed since " + bucketName + + " does not exist"); + response.setStatus(404); + return; + } + + S3PolicyContext context = new S3PolicyContext( + PolicyActions.ListBucketMultipartUploads, bucketName); + context.setEvalParam(ConditionKeys.Prefix, prefix); + context.setEvalParam(ConditionKeys.Delimiter, delimiter); + S3Engine.verifyAccess(context, "SBucket", bucket.getId(), + SAcl.PERMISSION_READ); + + // [B] Query the multipart table to get the list of current uploads + try { + MultipartLoadDao uploadDao = new MultipartLoadDao(); + OrderedPair result = uploadDao + .getInitiatedUploads(bucketName, maxUploads, prefix, + keyMarker, uploadIdMarker); + uploads = result.getFirst(); + isTruncated = result.getSecond().booleanValue(); + } catch (Exception e) { + logger.error( + "List Multipart Uploads failed due to " + e.getMessage(), e); + response.setStatus(500); + } + + StringBuffer xml = new StringBuffer(); + xml.append(""); + xml.append(""); + xml.append("").append(bucketName).append(""); + xml.append("").append((null == keyMarker ? "" : keyMarker)) + .append(""); + xml.append("") + .append((null == uploadIdMarker ? "" : uploadIdMarker)) + .append(""); + + // [C] Construct the contents of the element + StringBuffer partsList = new StringBuffer(); + for (int i = 0; i < uploads.length; i++) { + onePart = uploads[i]; + if (null == onePart) + break; + + if (delimiter != null && !delimiter.isEmpty()) { + // -> is this available only in the CommonPrefixes element? + if (StringHelper.substringInBetween(onePart.getKey(), prefix, + delimiter) != null) + continue; + } + + nextKey = onePart.getKey(); + nextUploadId = onePart.getId(); + partsList.append(""); + partsList.append("").append(nextKey).append(""); + partsList.append("").append(nextUploadId) + .append(""); + partsList.append(""); + partsList.append("").append(onePart.getAccessKey()) + .append(""); + partsList.append(""); + partsList.append(""); + partsList.append(""); + partsList.append("").append(onePart.getAccessKey()) + .append(""); + partsList.append(""); + partsList.append(""); + partsList.append("STANDARD"); + partsList + .append("") + .append(DatatypeConverter.printDateTime(onePart + .getLastModified())).append(""); + partsList.append(""); + } + + // [D] Construct the contents of the elements (if any) + for (int i = 0; i < uploads.length; i++) { + onePart = uploads[i]; + if (null == onePart) + break; + + if (delimiter != null && !delimiter.isEmpty()) { + String subName = StringHelper.substringInBetween( + onePart.getKey(), prefix, delimiter); + if (subName != null) { + partsList.append(""); + partsList.append(""); + if (prefix != null && prefix.length() > 0) + partsList.append(prefix + delimiter + subName); + else + partsList.append(subName); + partsList.append(""); + partsList.append(""); + } + } + } + + // [D] Finish off the response + xml.append("").append((null == nextKey ? "" : nextKey)) + .append(""); + xml.append("") + .append((0 == nextUploadId ? "" : nextUploadId)) + .append(""); + xml.append("").append(maxUploads).append(""); + xml.append("").append(isTruncated) + .append(""); + + xml.append(partsList.toString()); + xml.append(""); + + response.setStatus(200); + response.setContentType("text/xml; charset=UTF-8"); + S3RestServlet.endResponse(response, xml.toString()); + } + + private String streamToString( InputStream is ) throws IOException + { + int n = 0; + + if ( null != is ) + { + Writer writer = new StringWriter(); + char[] buffer = new char[1024]; + try { + Reader reader = new BufferedReader( new InputStreamReader(is, "UTF-8")); + while ((n = reader.read(buffer)) != -1) writer.write(buffer, 0, n); + } + finally { + is.close(); + } + return writer.toString(); + } + else return null; } } diff --git a/awsapi/src/com/cloud/bridge/service/controller/s3/S3ObjectAction.java b/awsapi/src/com/cloud/bridge/service/controller/s3/S3ObjectAction.java index ee4cec65e41..89ccf59916c 100644 --- a/awsapi/src/com/cloud/bridge/service/controller/s3/S3ObjectAction.java +++ b/awsapi/src/com/cloud/bridge/service/controller/s3/S3ObjectAction.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.UUID; import javax.activation.DataHandler; +import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.bind.DatatypeConverter; @@ -46,11 +47,9 @@ import com.amazon.s3.GetObjectAccessControlPolicyResponse; import com.cloud.bridge.io.MTOMAwareResultStreamWriter; import com.cloud.bridge.model.SAcl; import com.cloud.bridge.model.SAclVO; -import com.cloud.bridge.model.SBucket; import com.cloud.bridge.model.SBucketVO; import com.cloud.bridge.persist.dao.MultipartLoadDao; import com.cloud.bridge.persist.dao.SBucketDao; -import com.cloud.bridge.persist.dao.SBucketDaoImpl; import com.cloud.bridge.service.S3Constants; import com.cloud.bridge.service.S3RestServlet; import com.cloud.bridge.service.UserContext; @@ -68,1200 +67,1198 @@ import com.cloud.bridge.service.core.s3.S3GetObjectResponse; import com.cloud.bridge.service.core.s3.S3Grant; import com.cloud.bridge.service.core.s3.S3MetaDataEntry; import com.cloud.bridge.service.core.s3.S3MultipartPart; +import com.cloud.bridge.service.core.s3.S3PolicyAction.PolicyActions; import com.cloud.bridge.service.core.s3.S3PolicyContext; import com.cloud.bridge.service.core.s3.S3PutObjectInlineRequest; import com.cloud.bridge.service.core.s3.S3PutObjectInlineResponse; -import com.cloud.bridge.service.core.s3.S3PutObjectRequest; import com.cloud.bridge.service.core.s3.S3Response; -import com.cloud.bridge.service.core.s3.S3SetBucketAccessControlPolicyRequest; import com.cloud.bridge.service.core.s3.S3SetObjectAccessControlPolicyRequest; -import com.cloud.bridge.service.core.s3.S3PolicyAction.PolicyActions; import com.cloud.bridge.service.exception.PermissionDeniedException; import com.cloud.bridge.util.Converter; import com.cloud.bridge.util.DateHelper; import com.cloud.bridge.util.HeaderParam; -import com.cloud.bridge.util.ServletRequestDataSource; import com.cloud.bridge.util.OrderedPair; -import com.cloud.utils.component.ComponentLocator; +import com.cloud.bridge.util.ServletRequestDataSource; public class S3ObjectAction implements ServletAction { protected final static Logger logger = Logger.getLogger(S3ObjectAction.class); - protected final SBucketDao bucketDao = ComponentLocator.inject(SBucketDaoImpl.class); + @Inject SBucketDao bucketDao; private DocumentBuilderFactory dbf = null; - - public S3ObjectAction() { - dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware( true ); - } + public S3ObjectAction() { + dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware( true ); - public void execute(HttpServletRequest request, HttpServletResponse response) - throws IOException, XMLStreamException - { - String method = request.getMethod(); - String queryString = request.getQueryString(); - String copy = null; - - response.addHeader( "x-amz-request-id", UUID.randomUUID().toString()); - - if ( method.equalsIgnoreCase( "GET" )) - { - if ( queryString != null && queryString.length() > 0 ) - { - if (queryString.contains("acl")) executeGetObjectAcl(request, response); - else if (queryString.contains("uploadId")) executeListUploadParts(request, response); - else executeGetObject(request, response); - } - else executeGetObject(request, response); - } - else if (method.equalsIgnoreCase( "PUT" )) - { - if ( queryString != null && queryString.length() > 0 ) - { - if (queryString.contains("acl")) executePutObjectAcl(request, response); - else if (queryString.contains("partNumber")) executeUploadPart(request, response); - else executePutObject(request, response); - } - else if ( null != (copy = request.getHeader( "x-amz-copy-source" ))) - { - executeCopyObject(request, response, copy.trim()); - } - else executePutObject(request, response); - } - else if (method.equalsIgnoreCase( "DELETE" )) - { - if ( queryString != null && queryString.length() > 0 ) - { - if (queryString.contains("uploadId")) executeAbortMultipartUpload(request, response); - else executeDeleteObject(request, response); - } - else executeDeleteObject(request, response); - } - else if (method.equalsIgnoreCase( "HEAD" )) - { - executeHeadObject(request, response); - } - else if (method.equalsIgnoreCase( "POST" )) - { - if ( queryString != null && queryString.length() > 0 ) - { - if (queryString.contains("uploads")) executeInitiateMultipartUpload(request, response); - else if (queryString.contains("uploadId")) executeCompleteMultipartUpload(request, response); - } - else if ( request.getAttribute(S3Constants.PLAIN_POST_ACCESS_KEY) !=null ) - executePlainPostObject (request, response); - // TODO - Having implemented the request, now provide an informative HTML page response - else - executePostObject(request, response); - } - else throw new IllegalArgumentException( "Unsupported method in REST request"); - } + } - - private void executeCopyObject(HttpServletRequest request, HttpServletResponse response, String copy) - throws IOException, XMLStreamException - { + @Override + public void execute(HttpServletRequest request, HttpServletResponse response) + throws IOException, XMLStreamException + { + String method = request.getMethod(); + String queryString = request.getQueryString(); + String copy = null; + + response.addHeader( "x-amz-request-id", UUID.randomUUID().toString()); + + if ( method.equalsIgnoreCase( "GET" )) + { + if ( queryString != null && queryString.length() > 0 ) + { + if (queryString.contains("acl")) executeGetObjectAcl(request, response); + else if (queryString.contains("uploadId")) executeListUploadParts(request, response); + else executeGetObject(request, response); + } + else executeGetObject(request, response); + } + else if (method.equalsIgnoreCase( "PUT" )) + { + if ( queryString != null && queryString.length() > 0 ) + { + if (queryString.contains("acl")) executePutObjectAcl(request, response); + else if (queryString.contains("partNumber")) executeUploadPart(request, response); + else executePutObject(request, response); + } + else if ( null != (copy = request.getHeader( "x-amz-copy-source" ))) + { + executeCopyObject(request, response, copy.trim()); + } + else executePutObject(request, response); + } + else if (method.equalsIgnoreCase( "DELETE" )) + { + if ( queryString != null && queryString.length() > 0 ) + { + if (queryString.contains("uploadId")) executeAbortMultipartUpload(request, response); + else executeDeleteObject(request, response); + } + else executeDeleteObject(request, response); + } + else if (method.equalsIgnoreCase( "HEAD" )) + { + executeHeadObject(request, response); + } + else if (method.equalsIgnoreCase( "POST" )) + { + if ( queryString != null && queryString.length() > 0 ) + { + if (queryString.contains("uploads")) executeInitiateMultipartUpload(request, response); + else if (queryString.contains("uploadId")) executeCompleteMultipartUpload(request, response); + } + else if ( request.getAttribute(S3Constants.PLAIN_POST_ACCESS_KEY) !=null ) + executePlainPostObject (request, response); + // TODO - Having implemented the request, now provide an informative HTML page response + else + executePostObject(request, response); + } + else throw new IllegalArgumentException( "Unsupported method in REST request"); + } + + + private void executeCopyObject(HttpServletRequest request, HttpServletResponse response, String copy) + throws IOException, XMLStreamException + { S3CopyObjectRequest engineRequest = new S3CopyObjectRequest(); String versionId = null; - - String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY); - String sourceBucketName = null; - String sourceKey = null; - // [A] Parse the x-amz-copy-source header into usable pieces - // Check to find a ?versionId= value if any - int index = copy.indexOf( '?' ); - if (-1 != index) - { - versionId = copy.substring( index+1 ); - if (versionId.startsWith( "versionId=" )) engineRequest.setVersion( versionId.substring( 10 )); - copy = copy.substring( 0, index ); - } - - // The value of copy should look like: "bucket-name/object-name" - index = copy.indexOf( '/' ); - - // In case it looks like "/bucket-name/object-name" discard a leading '/' if it exists - if ( 0 == index ) - { - copy = copy.substring(1); - index = copy.indexOf( '/' ); - } - - if ( -1 == index ) - throw new IllegalArgumentException( "Invalid x-amz-copy-source header value [" + copy + "]" ); - - sourceBucketName = copy.substring( 0, index ); - sourceKey = copy.substring( index+1 ); - - - // [B] Set the object used in the SOAP request so it can do the bulk of the work for us + String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY); + String sourceBucketName = null; + String sourceKey = null; + + // [A] Parse the x-amz-copy-source header into usable pieces + // Check to find a ?versionId= value if any + int index = copy.indexOf( '?' ); + if (-1 != index) + { + versionId = copy.substring( index+1 ); + if (versionId.startsWith( "versionId=" )) engineRequest.setVersion( versionId.substring( 10 )); + copy = copy.substring( 0, index ); + } + + // The value of copy should look like: "bucket-name/object-name" + index = copy.indexOf( '/' ); + + // In case it looks like "/bucket-name/object-name" discard a leading '/' if it exists + if ( 0 == index ) + { + copy = copy.substring(1); + index = copy.indexOf( '/' ); + } + + if ( -1 == index ) + throw new IllegalArgumentException( "Invalid x-amz-copy-source header value [" + copy + "]" ); + + sourceBucketName = copy.substring( 0, index ); + sourceKey = copy.substring( index+1 ); + + + // [B] Set the object used in the SOAP request so it can do the bulk of the work for us engineRequest.setSourceBucketName( sourceBucketName ); engineRequest.setSourceKey( sourceKey ); engineRequest.setDestinationBucketName( bucketName ); engineRequest.setDestinationKey( key ); - + engineRequest.setDataDirective( request.getHeader( "x-amz-metadata-directive" )); - engineRequest.setMetaEntries( extractMetaData( request )); - engineRequest.setCannedAccess( request.getHeader( "x-amz-acl" )); - engineRequest.setConditions( conditionalRequest( request, true )); - - - // [C] Do the actual work and return the result - S3CopyObjectResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest( engineRequest ); - + engineRequest.setMetaEntries( extractMetaData( request )); + engineRequest.setCannedAccess( request.getHeader( "x-amz-acl" )); + engineRequest.setConditions( conditionalRequest( request, true )); + + + // [C] Do the actual work and return the result + S3CopyObjectResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest( engineRequest ); + versionId = engineResponse.getCopyVersion(); if (null != versionId) response.addHeader( "x-amz-copy-source-version-id", versionId ); versionId = engineResponse.getPutVersion(); if (null != versionId) response.addHeader( "x-amz-version-id", versionId ); - - // To allow the copy object result to be serialized via Axiom classes - CopyObjectResponse allBuckets = S3SerializableServiceImplementation.toCopyObjectResponse( engineResponse ); - - OutputStream outputStream = response.getOutputStream(); - response.setStatus(200); - response.setContentType("application/xml"); - // The content-type literally should be "application/xml; charset=UTF-8" - // but any compliant JVM supplies utf-8 by default; - - MTOMAwareResultStreamWriter resultWriter = new MTOMAwareResultStreamWriter ("CopyObjectResult", outputStream ); - resultWriter.startWrite(); - resultWriter.writeout(allBuckets); - resultWriter.stopWrite(); - } + // To allow the copy object result to be serialized via Axiom classes + CopyObjectResponse allBuckets = S3SerializableServiceImplementation.toCopyObjectResponse( engineResponse ); - private void executeGetObjectAcl(HttpServletRequest request, HttpServletResponse response) throws IOException, XMLStreamException - { - String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY); + OutputStream outputStream = response.getOutputStream(); + response.setStatus(200); + response.setContentType("application/xml"); + // The content-type literally should be "application/xml; charset=UTF-8" + // but any compliant JVM supplies utf-8 by default; - S3GetObjectAccessControlPolicyRequest engineRequest = new S3GetObjectAccessControlPolicyRequest(); - engineRequest.setBucketName( bucketName ); - engineRequest.setKey( key ); - - // -> is this a request for a specific version of the object? look for "versionId=" in the query string - String queryString = request.getQueryString(); - if (null != queryString) engineRequest.setVersion( returnParameter( queryString, "versionId=" )); + MTOMAwareResultStreamWriter resultWriter = new MTOMAwareResultStreamWriter ("CopyObjectResult", outputStream ); + resultWriter.startWrite(); + resultWriter.writeout(allBuckets); + resultWriter.stopWrite(); - S3AccessControlPolicy engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); - int resultCode = engineResponse.getResultCode(); - if (200 != resultCode) { - response.setStatus( resultCode ); - return; - } - String version = engineResponse.getVersion(); - if (null != version) response.addHeader( "x-amz-version-id", version ); - - - // To allow the get object acl policy result to be serialized via Axiom classes - GetObjectAccessControlPolicyResponse onePolicy = S3SerializableServiceImplementation.toGetObjectAccessControlPolicyResponse( engineResponse ); - - OutputStream outputStream = response.getOutputStream(); - response.setStatus(200); - response.setContentType("application/xml"); - // The content-type literally should be "application/xml; charset=UTF-8" - // but any compliant JVM supplies utf-8 by default; - - MTOMAwareResultStreamWriter resultWriter = new MTOMAwareResultStreamWriter ("GetObjectAccessControlPolicyResult", outputStream ); - resultWriter.startWrite(); - resultWriter.writeout(onePolicy); - resultWriter.stopWrite(); - } - - private void executePutObjectAcl(HttpServletRequest request, HttpServletResponse response) throws IOException - { - // [A] Determine that there is an applicable bucket which might have an ACL set - - String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY); - - SBucketVO bucket = bucketDao.getByName( bucketName ); - String owner = null; - if ( null != bucket ) - owner = bucket.getOwnerCanonicalId(); - if (null == owner) - { - logger.error( "ACL update failed since " + bucketName + " does not exist" ); - throw new IOException("ACL update failed"); - } - if (null == key) - { - logger.error( "ACL update failed since " + bucketName + " does not contain the expected key" ); - throw new IOException("ACL update failed"); - } - - // [B] Obtain the grant request which applies to the acl request string. This latter is supplied as the value of the x-amz-acl header. - - S3SetObjectAccessControlPolicyRequest engineRequest = new S3SetObjectAccessControlPolicyRequest(); - S3Grant grantRequest = new S3Grant(); - S3AccessControlList aclRequest = new S3AccessControlList(); - - String aclRequestString = request.getHeader("x-amz-acl"); - OrderedPair accessControlsForObjectOwner = SAclVO.getCannedAccessControls(aclRequestString,"SObject"); - grantRequest.setPermission(accessControlsForObjectOwner.getFirst()); - grantRequest.setGrantee(accessControlsForObjectOwner.getSecond()); - grantRequest.setCanonicalUserID(owner); - aclRequest.addGrant(grantRequest); - engineRequest.setAcl(aclRequest); - engineRequest.setBucketName(bucketName); - engineRequest.setKey(key); - - - // [C] Allow an S3Engine to handle the S3SetObjectAccessControlPolicyRequest - S3Response engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); - response.setStatus( engineResponse.getResultCode()); - - } + } - private void executeGetObject(HttpServletRequest request, HttpServletResponse response) throws IOException - { - String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); - - - S3GetObjectRequest engineRequest = new S3GetObjectRequest(); - engineRequest.setBucketName(bucket); - engineRequest.setKey(key); - engineRequest.setInlineData(true); - engineRequest.setReturnData(true); - //engineRequest.setReturnMetadata(true); - engineRequest = setRequestByteRange( request, engineRequest ); - - // -> is this a request for a specific version of the object? look for "versionId=" in the query string - String queryString = request.getQueryString(); - if (null != queryString) engineRequest.setVersion( returnParameter( queryString, "versionId=" )); - - S3GetObjectResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest( engineRequest ); - response.setStatus( engineResponse.getResultCode()); - - if (engineResponse.getResultCode() >=400 ) { - return; - } - String deleteMarker = engineResponse.getDeleteMarker(); - if ( null != deleteMarker ) { - response.addHeader( "x-amz-delete-marker", "true" ); - response.addHeader( "x-amz-version-id", deleteMarker ); - } - else { - String version = engineResponse.getVersion(); - if (null != version) response.addHeader( "x-amz-version-id", version ); - } - - // -> was the get conditional? - if (!conditionPassed( request, response, engineResponse.getLastModified().getTime(), engineResponse.getETag())) - return; - - - // -> is there data to return - // -> from the Amazon REST documentation it appears that Meta data is only returned as part of a HEAD request - //returnMetaData( engineResponse, response ); - - DataHandler dataHandler = engineResponse.getData(); - if (dataHandler != null) { - response.addHeader("ETag", "\"" + engineResponse.getETag() + "\""); - response.addHeader("Last-Modified", DateHelper.getDateDisplayString( - DateHelper.GMT_TIMEZONE, engineResponse.getLastModified().getTime(), "E, d MMM yyyy HH:mm:ss z")); - - response.setContentLength((int)engineResponse.getContentLength()); - S3RestServlet.writeResponse(response, dataHandler.getInputStream()); - } - } - - private void executePutObject(HttpServletRequest request, HttpServletResponse response) throws IOException - { - String continueHeader = request.getHeader( "Expect" ); - if (continueHeader != null && continueHeader.equalsIgnoreCase("100-continue")) { - S3RestServlet.writeResponse(response, "HTTP/1.1 100 Continue\r\n"); - } - - long contentLength = Converter.toLong(request.getHeader("Content-Length"), 0); - - String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); - S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest(); - engineRequest.setBucketName(bucket); - engineRequest.setKey(key); - engineRequest.setContentLength(contentLength); - engineRequest.setMetaEntries( extractMetaData( request )); - engineRequest.setCannedAccess( request.getHeader( "x-amz-acl" )); - - DataHandler dataHandler = new DataHandler(new ServletRequestDataSource(request)); - engineRequest.setData(dataHandler); - - S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); - response.setHeader("ETag", "\"" + engineResponse.getETag() + "\""); - String version = engineResponse.getVersion(); - if (null != version) response.addHeader( "x-amz-version-id", version ); - } - - /** - * Once versioining is turned on then to delete an object requires specifying a version - * parameter. A deletion marker is set once versioning is turned on in a bucket. - */ - private void executeDeleteObject(HttpServletRequest request, HttpServletResponse response) throws IOException - { - String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); - - S3DeleteObjectRequest engineRequest = new S3DeleteObjectRequest(); - engineRequest.setBucketName(bucket); - engineRequest.setKey(key); - - // -> is this a request for a specific version of the object? look for "versionId=" in the query string - String queryString = request.getQueryString(); - if (null != queryString) engineRequest.setVersion( returnParameter( queryString, "versionId=" )); - - S3Response engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest( engineRequest ); - - response.setStatus( engineResponse.getResultCode()); - String version = engineRequest.getVersion(); - if (null != version) response.addHeader( "x-amz-version-id", version ); - } - - /* - * The purpose of a plain POST operation is to add an object to a specified bucket using HTML forms. - * The capability is for developer and tester convenience providing a simple browser-based upload - * feature as an alternative to using PUTs. - * In the case of PUTs the upload information is passed through HTTP headers. However in the case of a - * POST this information must be supplied as form fields. Many of these are mandatory or otherwise - * the POST request will be rejected. - * The requester using the HTML page must submit valid credentials sufficient for checking that - * the bucket to which the object is to be added has WRITE permission for that user. The AWS access - * key field on the form is taken to be synonymous with the user canonical ID for this purpose. - */ - private void executePlainPostObject(HttpServletRequest request, HttpServletResponse response) throws IOException - { - String continueHeader = request.getHeader( "Expect" ); - if (continueHeader != null && continueHeader.equalsIgnoreCase("100-continue")) { - S3RestServlet.writeResponse(response, "HTTP/1.1 100 Continue\r\n"); - } - - long contentLength = Converter.toLong(request.getHeader("Content-Length"), 0); - - String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); - String accessKey = (String) request.getAttribute(S3Constants.PLAIN_POST_ACCESS_KEY); - String signature = (String) request.getAttribute(S3Constants.PLAIN_POST_SIGNATURE); - S3Grant grant = new S3Grant(); - grant.setCanonicalUserID(accessKey); - grant.setGrantee(SAcl.GRANTEE_USER); - grant.setPermission(SAcl.PERMISSION_FULL); - S3AccessControlList acl = new S3AccessControlList(); - acl.addGrant(grant); - S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest(); - engineRequest.setBucketName(bucket); - engineRequest.setKey(key); - engineRequest.setAcl(acl); - engineRequest.setContentLength(contentLength); - engineRequest.setMetaEntries( extractMetaData( request )); - engineRequest.setCannedAccess( request.getHeader( "x-amz-acl" )); - - DataHandler dataHandler = new DataHandler(new ServletRequestDataSource(request)); - engineRequest.setData(dataHandler); - - S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); - response.setHeader("ETag", "\"" + engineResponse.getETag() + "\""); - String version = engineResponse.getVersion(); - if (null != version) response.addHeader( "x-amz-version-id", version ); - } - - - private void executeHeadObject(HttpServletRequest request, HttpServletResponse response) throws IOException - { - String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); - - S3GetObjectRequest engineRequest = new S3GetObjectRequest(); - engineRequest.setBucketName(bucket); - engineRequest.setKey(key); - engineRequest.setInlineData(true); // -> need to set so we get ETag etc returned - engineRequest.setReturnData(true); - engineRequest.setReturnMetadata(true); - engineRequest = setRequestByteRange( request, engineRequest ); - - // -> is this a request for a specific version of the object? look for "versionId=" in the query string - String queryString = request.getQueryString(); - if (null != queryString) engineRequest.setVersion( returnParameter( queryString, "versionId=" )); - - S3GetObjectResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest( engineRequest ); - response.setStatus( engineResponse.getResultCode()); - - //bucket lookup for non-existance key - - if ( engineResponse.getResultCode() == 404 ) - return; - - String deleteMarker = engineResponse.getDeleteMarker(); - if ( null != deleteMarker ) { - response.addHeader( "x-amz-delete-marker", "true" ); - response.addHeader( "x-amz-version-id", deleteMarker ); - } - else { - String version = engineResponse.getVersion(); - if (null != version) response.addHeader( "x-amz-version-id", version ); - } - - // -> was the head request conditional? - if (!conditionPassed( request, response, engineResponse.getLastModified().getTime(), engineResponse.getETag())) - return; - - - // -> for a head request we return everything except the data - returnMetaData( engineResponse, response ); - - DataHandler dataHandler = engineResponse.getData(); - if (dataHandler != null) { - response.addHeader("ETag", "\"" + engineResponse.getETag() + "\""); - response.addHeader("Last-Modified", DateHelper.getDateDisplayString( - DateHelper.GMT_TIMEZONE, engineResponse.getLastModified().getTime(), "E, d MMM yyyy HH:mm:ss z")); - - response.setContentLength((int)engineResponse.getContentLength()); - } - } - - // There is a problem with POST since the 'Signature' and 'AccessKey' parameters are not - // determined until we hit this function (i.e., they are encoded in the body of the message - // they are not HTTP request headers). All the values we used to get in the request headers - // are not encoded in the request body. - // - // add ETag header computed as Base64 MD5 whenever object is uploaded or updated - // - private void executePostObject( HttpServletRequest request, HttpServletResponse response ) throws IOException - { - String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String contentType = request.getHeader( "Content-Type" ); - int boundaryIndex = contentType.indexOf( "boundary=" ); - String boundary = "--" + (contentType.substring( boundaryIndex + 9 )); - String lastBoundary = boundary + "--"; - - InputStreamReader isr = new InputStreamReader( request.getInputStream()); - BufferedReader br = new BufferedReader( isr ); - - StringBuffer temp = new StringBuffer(); - String oneLine = null; - String name = null; - String value = null; - String metaName = null; // -> after stripped off the x-amz-meta- - boolean isMetaTag = false; - int countMeta = 0; - int state = 0; - - // [A] First parse all the parts out of the POST request and message body - // -> bucket name is still encoded in a Host header - S3AuthParams params = new S3AuthParams(); - List metaSet = new ArrayList(); - S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest(); - engineRequest.setBucketName( bucket ); - - // -> the last body part contains the content that is used to write the S3 object, all - // other body parts are header values - while( null != (oneLine = br.readLine())) - { - if ( oneLine.startsWith( lastBoundary )) - { - // -> this is the data of the object to put - if (0 < temp.length()) - { - value = temp.toString(); - temp.setLength( 0 ); - - engineRequest.setContentLength( value.length()); - engineRequest.setDataAsString( value ); - } - break; - } - else if ( oneLine.startsWith( boundary )) - { - // -> this is the header data - if (0 < temp.length()) - { - value = temp.toString().trim(); - temp.setLength( 0 ); - //System.out.println( "param: " + name + " = " + value ); - - if (name.equalsIgnoreCase( "key" )) { - engineRequest.setKey( value ); - } - else if (name.equalsIgnoreCase( "x-amz-acl" )) { - engineRequest.setCannedAccess( value ); - } - else if (isMetaTag) { - S3MetaDataEntry oneMeta = new S3MetaDataEntry(); - oneMeta.setName( metaName ); - oneMeta.setValue( value ); - metaSet.add( oneMeta ); - countMeta++; - metaName = null; - } - - // -> build up the headers so we can do authentication on this POST - HeaderParam oneHeader = new HeaderParam(); - oneHeader.setName( name ); - oneHeader.setValue( value ); - params.addHeader( oneHeader ); - } - state = 1; - } - else if (1 == state && 0 == oneLine.length()) - { - // -> data of a body part starts here - state = 2; - } - else if (1 == state) - { - // -> the name of the 'name-value' pair is encoded in the Content-Disposition header - if (oneLine.startsWith( "Content-Disposition: form-data;")) - { - isMetaTag = false; - int nameOffset = oneLine.indexOf( "name=" ); - if (-1 != nameOffset) - { - name = oneLine.substring( nameOffset+5 ); - if (name.startsWith( "\"" )) name = name.substring( 1 ); - if (name.endsWith( "\"" )) name = name.substring( 0, name.length()-1 ); - name = name.trim(); - - if (name.startsWith( "x-amz-meta-" )) { - metaName = name.substring( 11 ); - isMetaTag = true; - } - } - } - } - else if (2 == state) - { - // -> the body parts data may take up multiple lines - //System.out.println( oneLine.length() + " body data: " + oneLine ); - temp.append( oneLine ); - } -// else System.out.println( oneLine.length() + " preamble: " + oneLine ); - } - - - // [B] Authenticate the POST request after we have all the headers - try { - S3RestServlet.authenticateRequest( request, params ); - } - catch( Exception e ) { - throw new IOException( e.toString()); - } - - // [C] Perform the request - if (0 < countMeta) engineRequest.setMetaEntries( metaSet.toArray(new S3MetaDataEntry[0])); - S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest( engineRequest ); - response.setHeader("ETag", "\"" + engineResponse.getETag() + "\""); - String version = engineResponse.getVersion(); - if (null != version) response.addHeader( "x-amz-version-id", version ); - } - - /** - * Save all the information about the multipart upload request in the database so once it is finished - * (in the future) we can create the real S3 object. - * - * @throws IOException - */ - private void executeInitiateMultipartUpload( HttpServletRequest request, HttpServletResponse response ) throws IOException + private void executeGetObjectAcl(HttpServletRequest request, HttpServletResponse response) throws IOException, XMLStreamException { - // This request is via a POST which typically has its auth parameters inside the message - try { - S3RestServlet.authenticateRequest( request, S3RestServlet.extractRequestHeaders( request )); - } - catch( Exception e ) { - throw new IOException( e.toString()); - } + String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY); + + S3GetObjectAccessControlPolicyRequest engineRequest = new S3GetObjectAccessControlPolicyRequest(); + engineRequest.setBucketName( bucketName ); + engineRequest.setKey( key ); + + // -> is this a request for a specific version of the object? look for "versionId=" in the query string + String queryString = request.getQueryString(); + if (null != queryString) engineRequest.setVersion( returnParameter( queryString, "versionId=" )); + + S3AccessControlPolicy engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); + int resultCode = engineResponse.getResultCode(); + if (200 != resultCode) { + response.setStatus( resultCode ); + return; + } + String version = engineResponse.getVersion(); + if (null != version) response.addHeader( "x-amz-version-id", version ); + + + // To allow the get object acl policy result to be serialized via Axiom classes + GetObjectAccessControlPolicyResponse onePolicy = S3SerializableServiceImplementation.toGetObjectAccessControlPolicyResponse( engineResponse ); + + OutputStream outputStream = response.getOutputStream(); + response.setStatus(200); + response.setContentType("application/xml"); + // The content-type literally should be "application/xml; charset=UTF-8" + // but any compliant JVM supplies utf-8 by default; + + MTOMAwareResultStreamWriter resultWriter = new MTOMAwareResultStreamWriter ("GetObjectAccessControlPolicyResult", outputStream ); + resultWriter.startWrite(); + resultWriter.writeout(onePolicy); + resultWriter.stopWrite(); + } + + private void executePutObjectAcl(HttpServletRequest request, HttpServletResponse response) throws IOException + { + // [A] Determine that there is an applicable bucket which might have an ACL set + + String bucketName = (String)request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String key = (String)request.getAttribute(S3Constants.OBJECT_ATTR_KEY); + + SBucketVO bucket = bucketDao.getByName( bucketName ); + String owner = null; + if ( null != bucket ) + owner = bucket.getOwnerCanonicalId(); + if (null == owner) + { + logger.error( "ACL update failed since " + bucketName + " does not exist" ); + throw new IOException("ACL update failed"); + } + if (null == key) + { + logger.error( "ACL update failed since " + bucketName + " does not contain the expected key" ); + throw new IOException("ACL update failed"); + } + + // [B] Obtain the grant request which applies to the acl request string. This latter is supplied as the value of the x-amz-acl header. + + S3SetObjectAccessControlPolicyRequest engineRequest = new S3SetObjectAccessControlPolicyRequest(); + S3Grant grantRequest = new S3Grant(); + S3AccessControlList aclRequest = new S3AccessControlList(); + + String aclRequestString = request.getHeader("x-amz-acl"); + OrderedPair accessControlsForObjectOwner = SAclVO.getCannedAccessControls(aclRequestString,"SObject"); + grantRequest.setPermission(accessControlsForObjectOwner.getFirst()); + grantRequest.setGrantee(accessControlsForObjectOwner.getSecond()); + grantRequest.setCanonicalUserID(owner); + aclRequest.addGrant(grantRequest); + engineRequest.setAcl(aclRequest); + engineRequest.setBucketName(bucketName); + engineRequest.setKey(key); + + + // [C] Allow an S3Engine to handle the S3SetObjectAccessControlPolicyRequest + S3Response engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); + response.setStatus( engineResponse.getResultCode()); + + } + + private void executeGetObject(HttpServletRequest request, HttpServletResponse response) throws IOException + { + String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); + + + S3GetObjectRequest engineRequest = new S3GetObjectRequest(); + engineRequest.setBucketName(bucket); + engineRequest.setKey(key); + engineRequest.setInlineData(true); + engineRequest.setReturnData(true); + //engineRequest.setReturnMetadata(true); + engineRequest = setRequestByteRange( request, engineRequest ); + + // -> is this a request for a specific version of the object? look for "versionId=" in the query string + String queryString = request.getQueryString(); + if (null != queryString) engineRequest.setVersion( returnParameter( queryString, "versionId=" )); + + S3GetObjectResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest( engineRequest ); + response.setStatus( engineResponse.getResultCode()); + + if (engineResponse.getResultCode() >=400 ) { + return; + } + String deleteMarker = engineResponse.getDeleteMarker(); + if ( null != deleteMarker ) { + response.addHeader( "x-amz-delete-marker", "true" ); + response.addHeader( "x-amz-version-id", deleteMarker ); + } + else { + String version = engineResponse.getVersion(); + if (null != version) response.addHeader( "x-amz-version-id", version ); + } + + // -> was the get conditional? + if (!conditionPassed( request, response, engineResponse.getLastModified().getTime(), engineResponse.getETag())) + return; + + + // -> is there data to return + // -> from the Amazon REST documentation it appears that Meta data is only returned as part of a HEAD request + //returnMetaData( engineResponse, response ); + + DataHandler dataHandler = engineResponse.getData(); + if (dataHandler != null) { + response.addHeader("ETag", "\"" + engineResponse.getETag() + "\""); + response.addHeader("Last-Modified", DateHelper.getDateDisplayString( + DateHelper.GMT_TIMEZONE, engineResponse.getLastModified().getTime(), "E, d MMM yyyy HH:mm:ss z")); + + response.setContentLength((int)engineResponse.getContentLength()); + S3RestServlet.writeResponse(response, dataHandler.getInputStream()); + } + } + + private void executePutObject(HttpServletRequest request, HttpServletResponse response) throws IOException + { + String continueHeader = request.getHeader( "Expect" ); + if (continueHeader != null && continueHeader.equalsIgnoreCase("100-continue")) { + S3RestServlet.writeResponse(response, "HTTP/1.1 100 Continue\r\n"); + } + + long contentLength = Converter.toLong(request.getHeader("Content-Length"), 0); + + String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); + S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest(); + engineRequest.setBucketName(bucket); + engineRequest.setKey(key); + engineRequest.setContentLength(contentLength); + engineRequest.setMetaEntries( extractMetaData( request )); + engineRequest.setCannedAccess( request.getHeader( "x-amz-acl" )); + + DataHandler dataHandler = new DataHandler(new ServletRequestDataSource(request)); + engineRequest.setData(dataHandler); + + S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); + response.setHeader("ETag", "\"" + engineResponse.getETag() + "\""); + String version = engineResponse.getVersion(); + if (null != version) response.addHeader( "x-amz-version-id", version ); + } + + /** + * Once versioining is turned on then to delete an object requires specifying a version + * parameter. A deletion marker is set once versioning is turned on in a bucket. + */ + private void executeDeleteObject(HttpServletRequest request, HttpServletResponse response) throws IOException + { + String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); + + S3DeleteObjectRequest engineRequest = new S3DeleteObjectRequest(); + engineRequest.setBucketName(bucket); + engineRequest.setKey(key); + + // -> is this a request for a specific version of the object? look for "versionId=" in the query string + String queryString = request.getQueryString(); + if (null != queryString) engineRequest.setVersion( returnParameter( queryString, "versionId=" )); + + S3Response engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest( engineRequest ); + + response.setStatus( engineResponse.getResultCode()); + String version = engineRequest.getVersion(); + if (null != version) response.addHeader( "x-amz-version-id", version ); + } + + /* + * The purpose of a plain POST operation is to add an object to a specified bucket using HTML forms. + * The capability is for developer and tester convenience providing a simple browser-based upload + * feature as an alternative to using PUTs. + * In the case of PUTs the upload information is passed through HTTP headers. However in the case of a + * POST this information must be supplied as form fields. Many of these are mandatory or otherwise + * the POST request will be rejected. + * The requester using the HTML page must submit valid credentials sufficient for checking that + * the bucket to which the object is to be added has WRITE permission for that user. The AWS access + * key field on the form is taken to be synonymous with the user canonical ID for this purpose. + */ + private void executePlainPostObject(HttpServletRequest request, HttpServletResponse response) throws IOException + { + String continueHeader = request.getHeader( "Expect" ); + if (continueHeader != null && continueHeader.equalsIgnoreCase("100-continue")) { + S3RestServlet.writeResponse(response, "HTTP/1.1 100 Continue\r\n"); + } + + long contentLength = Converter.toLong(request.getHeader("Content-Length"), 0); + + String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); + String accessKey = (String) request.getAttribute(S3Constants.PLAIN_POST_ACCESS_KEY); + String signature = (String) request.getAttribute(S3Constants.PLAIN_POST_SIGNATURE); + S3Grant grant = new S3Grant(); + grant.setCanonicalUserID(accessKey); + grant.setGrantee(SAcl.GRANTEE_USER); + grant.setPermission(SAcl.PERMISSION_FULL); + S3AccessControlList acl = new S3AccessControlList(); + acl.addGrant(grant); + S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest(); + engineRequest.setBucketName(bucket); + engineRequest.setKey(key); + engineRequest.setAcl(acl); + engineRequest.setContentLength(contentLength); + engineRequest.setMetaEntries( extractMetaData( request )); + engineRequest.setCannedAccess( request.getHeader( "x-amz-acl" )); + + DataHandler dataHandler = new DataHandler(new ServletRequestDataSource(request)); + engineRequest.setData(dataHandler); + + S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest(engineRequest); + response.setHeader("ETag", "\"" + engineResponse.getETag() + "\""); + String version = engineResponse.getVersion(); + if (null != version) response.addHeader( "x-amz-version-id", version ); + } + + + private void executeHeadObject(HttpServletRequest request, HttpServletResponse response) throws IOException + { + String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); + + S3GetObjectRequest engineRequest = new S3GetObjectRequest(); + engineRequest.setBucketName(bucket); + engineRequest.setKey(key); + engineRequest.setInlineData(true); // -> need to set so we get ETag etc returned + engineRequest.setReturnData(true); + engineRequest.setReturnMetadata(true); + engineRequest = setRequestByteRange( request, engineRequest ); + + // -> is this a request for a specific version of the object? look for "versionId=" in the query string + String queryString = request.getQueryString(); + if (null != queryString) engineRequest.setVersion( returnParameter( queryString, "versionId=" )); + + S3GetObjectResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest( engineRequest ); + response.setStatus( engineResponse.getResultCode()); + + //bucket lookup for non-existance key + + if ( engineResponse.getResultCode() == 404 ) + return; + + String deleteMarker = engineResponse.getDeleteMarker(); + if ( null != deleteMarker ) { + response.addHeader( "x-amz-delete-marker", "true" ); + response.addHeader( "x-amz-version-id", deleteMarker ); + } + else { + String version = engineResponse.getVersion(); + if (null != version) response.addHeader( "x-amz-version-id", version ); + } + + // -> was the head request conditional? + if (!conditionPassed( request, response, engineResponse.getLastModified().getTime(), engineResponse.getETag())) + return; + + + // -> for a head request we return everything except the data + returnMetaData( engineResponse, response ); + + DataHandler dataHandler = engineResponse.getData(); + if (dataHandler != null) { + response.addHeader("ETag", "\"" + engineResponse.getETag() + "\""); + response.addHeader("Last-Modified", DateHelper.getDateDisplayString( + DateHelper.GMT_TIMEZONE, engineResponse.getLastModified().getTime(), "E, d MMM yyyy HH:mm:ss z")); + + response.setContentLength((int)engineResponse.getContentLength()); + } + } + + // There is a problem with POST since the 'Signature' and 'AccessKey' parameters are not + // determined until we hit this function (i.e., they are encoded in the body of the message + // they are not HTTP request headers). All the values we used to get in the request headers + // are not encoded in the request body. + // + // add ETag header computed as Base64 MD5 whenever object is uploaded or updated + // + private void executePostObject( HttpServletRequest request, HttpServletResponse response ) throws IOException + { + String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String contentType = request.getHeader( "Content-Type" ); + int boundaryIndex = contentType.indexOf( "boundary=" ); + String boundary = "--" + (contentType.substring( boundaryIndex + 9 )); + String lastBoundary = boundary + "--"; + + InputStreamReader isr = new InputStreamReader( request.getInputStream()); + BufferedReader br = new BufferedReader( isr ); + + StringBuffer temp = new StringBuffer(); + String oneLine = null; + String name = null; + String value = null; + String metaName = null; // -> after stripped off the x-amz-meta- + boolean isMetaTag = false; + int countMeta = 0; + int state = 0; + + // [A] First parse all the parts out of the POST request and message body + // -> bucket name is still encoded in a Host header + S3AuthParams params = new S3AuthParams(); + List metaSet = new ArrayList(); + S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest(); + engineRequest.setBucketName( bucket ); + + // -> the last body part contains the content that is used to write the S3 object, all + // other body parts are header values + while( null != (oneLine = br.readLine())) + { + if ( oneLine.startsWith( lastBoundary )) + { + // -> this is the data of the object to put + if (0 < temp.length()) + { + value = temp.toString(); + temp.setLength( 0 ); + + engineRequest.setContentLength( value.length()); + engineRequest.setDataAsString( value ); + } + break; + } + else if ( oneLine.startsWith( boundary )) + { + // -> this is the header data + if (0 < temp.length()) + { + value = temp.toString().trim(); + temp.setLength( 0 ); + //System.out.println( "param: " + name + " = " + value ); + + if (name.equalsIgnoreCase( "key" )) { + engineRequest.setKey( value ); + } + else if (name.equalsIgnoreCase( "x-amz-acl" )) { + engineRequest.setCannedAccess( value ); + } + else if (isMetaTag) { + S3MetaDataEntry oneMeta = new S3MetaDataEntry(); + oneMeta.setName( metaName ); + oneMeta.setValue( value ); + metaSet.add( oneMeta ); + countMeta++; + metaName = null; + } + + // -> build up the headers so we can do authentication on this POST + HeaderParam oneHeader = new HeaderParam(); + oneHeader.setName( name ); + oneHeader.setValue( value ); + params.addHeader( oneHeader ); + } + state = 1; + } + else if (1 == state && 0 == oneLine.length()) + { + // -> data of a body part starts here + state = 2; + } + else if (1 == state) + { + // -> the name of the 'name-value' pair is encoded in the Content-Disposition header + if (oneLine.startsWith( "Content-Disposition: form-data;")) + { + isMetaTag = false; + int nameOffset = oneLine.indexOf( "name=" ); + if (-1 != nameOffset) + { + name = oneLine.substring( nameOffset+5 ); + if (name.startsWith( "\"" )) name = name.substring( 1 ); + if (name.endsWith( "\"" )) name = name.substring( 0, name.length()-1 ); + name = name.trim(); + + if (name.startsWith( "x-amz-meta-" )) { + metaName = name.substring( 11 ); + isMetaTag = true; + } + } + } + } + else if (2 == state) + { + // -> the body parts data may take up multiple lines + //System.out.println( oneLine.length() + " body data: " + oneLine ); + temp.append( oneLine ); + } +// else System.out.println( oneLine.length() + " preamble: " + oneLine ); + } + + + // [B] Authenticate the POST request after we have all the headers + try { + S3RestServlet.authenticateRequest( request, params ); + } + catch( Exception e ) { + throw new IOException( e.toString()); + } + + // [C] Perform the request + if (0 < countMeta) engineRequest.setMetaEntries( metaSet.toArray(new S3MetaDataEntry[0])); + S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().handleRequest( engineRequest ); + response.setHeader("ETag", "\"" + engineResponse.getETag() + "\""); + String version = engineResponse.getVersion(); + if (null != version) response.addHeader( "x-amz-version-id", version ); + } + + /** + * Save all the information about the multipart upload request in the database so once it is finished + * (in the future) we can create the real S3 object. + * + * @throws IOException + */ + private void executeInitiateMultipartUpload( HttpServletRequest request, HttpServletResponse response ) throws IOException + { + // This request is via a POST which typically has its auth parameters inside the message + try { + S3RestServlet.authenticateRequest( request, S3RestServlet.extractRequestHeaders( request )); + } + catch( Exception e ) { + throw new IOException( e.toString()); + } + + String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); + String cannedAccess = request.getHeader( "x-amz-acl" ); + S3MetaDataEntry[] meta = extractMetaData( request ); - String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); - String cannedAccess = request.getHeader( "x-amz-acl" ); - S3MetaDataEntry[] meta = extractMetaData( request ); - // -> the S3 engine has easy access to all the privileged checking code - S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest(); - engineRequest.setBucketName(bucket); - engineRequest.setKey(key); - engineRequest.setCannedAccess( cannedAccess ); - engineRequest.setMetaEntries( meta ); - S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().initiateMultipartUpload( engineRequest ); - int result = engineResponse.getResultCode(); - response.setStatus( result ); + S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest(); + engineRequest.setBucketName(bucket); + engineRequest.setKey(key); + engineRequest.setCannedAccess( cannedAccess ); + engineRequest.setMetaEntries( meta ); + S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().initiateMultipartUpload( engineRequest ); + int result = engineResponse.getResultCode(); + response.setStatus( result ); if (200 != result) return; - + // -> there is no SOAP version of this function - StringBuffer xml = new StringBuffer(); + StringBuffer xml = new StringBuffer(); xml.append( "" ); xml.append( "" ); xml.append( "" ).append( bucket ).append( "" ); xml.append( "" ).append( key ).append( "" ); xml.append( "" ).append( engineResponse.getUploadId()).append( "" ); xml.append( "" ); - - response.setContentType("text/xml; charset=UTF-8"); - S3RestServlet.endResponse(response, xml.toString()); - } - - private void executeUploadPart( HttpServletRequest request, HttpServletResponse response ) throws IOException - { - String continueHeader = request.getHeader( "Expect" ); - if (continueHeader != null && continueHeader.equalsIgnoreCase("100-continue")) { - S3RestServlet.writeResponse(response, "HTTP/1.1 100 Continue\r\n"); - } - - String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); - int partNumber = -1; - int uploadId = -1; - long contentLength = Converter.toLong(request.getHeader("Content-Length"), 0); + response.setContentType("text/xml; charset=UTF-8"); + S3RestServlet.endResponse(response, xml.toString()); + } - String temp = request.getParameter("uploadId"); - if (null != temp) uploadId = Integer.parseInt( temp ); + private void executeUploadPart( HttpServletRequest request, HttpServletResponse response ) throws IOException + { + String continueHeader = request.getHeader( "Expect" ); + if (continueHeader != null && continueHeader.equalsIgnoreCase("100-continue")) { + S3RestServlet.writeResponse(response, "HTTP/1.1 100 Continue\r\n"); + } - temp = request.getParameter("partNumber"); - if (null != temp) partNumber = Integer.parseInt( temp ); - if (partNumber < 1 || partNumber > 10000) { - logger.error("uploadPart invalid part number " + partNumber ); - response.setStatus(416); + String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); + int partNumber = -1; + int uploadId = -1; + + long contentLength = Converter.toLong(request.getHeader("Content-Length"), 0); + + String temp = request.getParameter("uploadId"); + if (null != temp) uploadId = Integer.parseInt( temp ); + + temp = request.getParameter("partNumber"); + if (null != temp) partNumber = Integer.parseInt( temp ); + if (partNumber < 1 || partNumber > 10000) { + logger.error("uploadPart invalid part number " + partNumber ); + response.setStatus(416); return; - } - - // -> verification - try { - MultipartLoadDao uploadDao = new MultipartLoadDao(); - if (null == uploadDao.multipartExits( uploadId )) { - response.setStatus(404); - return; - } - - // -> another requirement is that only the upload initiator can upload parts - String initiator = uploadDao.getInitiator( uploadId ); - if (null == initiator || !initiator.equals( UserContext.current().getAccessKey())) { - response.setStatus(403); - return; - } - } - catch( Exception e ) { - logger.error("executeUploadPart failed due to " + e.getMessage(), e); - response.setStatus(500); - return; - } + } - S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest(); - engineRequest.setBucketName(bucket); - engineRequest.setKey(key); - engineRequest.setContentLength(contentLength); - DataHandler dataHandler = new DataHandler(new ServletRequestDataSource(request)); - engineRequest.setData(dataHandler); + // -> verification + try { + MultipartLoadDao uploadDao = new MultipartLoadDao(); + if (null == uploadDao.multipartExits( uploadId )) { + response.setStatus(404); + return; + } - S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().saveUploadPart( engineRequest, uploadId, partNumber ); - if (null != engineResponse.getETag()) response.setHeader("ETag", "\"" + engineResponse.getETag() + "\""); - response.setStatus(engineResponse.getResultCode()); - } - - /** - * This function is required to both parsing XML on the request and return XML as part of its result. - * - * @param request - * @param response - * @throws IOException - */ - private void executeCompleteMultipartUpload( HttpServletRequest request, HttpServletResponse response ) throws IOException - { - // [A] This request is via a POST which typically has its auth parameters inside the message - try { - S3RestServlet.authenticateRequest( request, S3RestServlet.extractRequestHeaders( request )); - } - catch( Exception e ) { - throw new IOException( e.toString()); - } + // -> another requirement is that only the upload initiator can upload parts + String initiator = uploadDao.getInitiator( uploadId ); + if (null == initiator || !initiator.equals( UserContext.current().getAccessKey())) { + response.setStatus(403); + return; + } + } + catch( Exception e ) { + logger.error("executeUploadPart failed due to " + e.getMessage(), e); + response.setStatus(500); + return; + } + + S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest(); + engineRequest.setBucketName(bucket); + engineRequest.setKey(key); + engineRequest.setContentLength(contentLength); + DataHandler dataHandler = new DataHandler(new ServletRequestDataSource(request)); + engineRequest.setData(dataHandler); + + S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().saveUploadPart( engineRequest, uploadId, partNumber ); + if (null != engineResponse.getETag()) response.setHeader("ETag", "\"" + engineResponse.getETag() + "\""); + response.setStatus(engineResponse.getResultCode()); + } + + /** + * This function is required to both parsing XML on the request and return XML as part of its result. + * + * @param request + * @param response + * @throws IOException + */ + private void executeCompleteMultipartUpload( HttpServletRequest request, HttpServletResponse response ) throws IOException + { + // [A] This request is via a POST which typically has its auth parameters inside the message + try { + S3RestServlet.authenticateRequest( request, S3RestServlet.extractRequestHeaders( request )); + } + catch( Exception e ) { + throw new IOException( e.toString()); + } + + String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); + S3MultipartPart[] parts = null; + S3MetaDataEntry[] meta = null; + String cannedAccess = null; + int uploadId = -1; - String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); - S3MultipartPart[] parts = null; - S3MetaDataEntry[] meta = null; - String cannedAccess = null; - int uploadId = -1; - // AWS S3 specifies that the keep alive connection is by sending whitespace characters until done - // Therefore the XML version prolog is prepended to the stream in advance + // Therefore the XML version prolog is prepended to the stream in advance OutputStream outputStream = response.getOutputStream(); outputStream.write("".getBytes()); - String temp = request.getParameter("uploadId"); - if (null != temp) uploadId = Integer.parseInt( temp ); - - - // [B] Look up all the uploaded body parts and related info - try { - MultipartLoadDao uploadDao = new MultipartLoadDao(); - if (null == uploadDao.multipartExits( uploadId )) { - response.setStatus(404); - returnErrorXML( 404, "NotFound", outputStream ); - return; - } - - // -> another requirement is that only the upload initiator can upload parts - String initiator = uploadDao.getInitiator( uploadId ); - if (null == initiator || !initiator.equals( UserContext.current().getAccessKey())) { - response.setStatus(403); - returnErrorXML( 403, "Forbidden", outputStream ); - return; - } - - parts = uploadDao.getParts( uploadId, 10000, 0 ); - meta = uploadDao.getMeta( uploadId ); - cannedAccess = uploadDao.getCannedAccess( uploadId ); - } - catch( Exception e ) { - logger.error("executeCompleteMultipartUpload failed due to " + e.getMessage(), e); - response.setStatus(500); - returnErrorXML( 500, "InternalError", outputStream ); - return; - } - - - // [C] Parse the given XML body part and perform error checking - OrderedPair match = verifyParts( request.getInputStream(), parts ); - if (200 != match.getFirst().intValue()) { - response.setStatus(match.getFirst().intValue()); - returnErrorXML( match.getFirst().intValue(), match.getSecond(), outputStream ); - return; - } + String temp = request.getParameter("uploadId"); + if (null != temp) uploadId = Integer.parseInt( temp ); - - // [D] Ask the engine to create a newly re-constituted object - S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest(); - engineRequest.setBucketName(bucket); - engineRequest.setKey(key); - engineRequest.setMetaEntries(meta); - engineRequest.setCannedAccess(cannedAccess); - S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().concatentateMultipartUploads( response, engineRequest, parts, outputStream ); - int result = engineResponse.getResultCode(); - // -> free all multipart state since we now have one concatentated object - if (200 == result) ServiceProvider.getInstance().getS3Engine().freeUploadParts( bucket, uploadId, false ); - - // If all successful then clean up all left over parts - // Notice that "" has already been written into the servlet output stream at the beginning of section [A] - if ( 200 == result ) - { - StringBuffer xml = new StringBuffer(); - xml.append( "" ); - xml.append( "" ).append( "http://" + bucket + ".s3.amazonaws.com/" + key ).append( "" ); - xml.append( "" ).append( bucket ).append( "" ); - xml.append( "" ).append( key ).append( "" ); - xml.append( "\"" ).append( engineResponse.getETag()).append( "\"" ); - xml.append( "" ); - String xmlString = xml.toString().replaceAll("^\\s+", ""); // Remove leading whitespace characters - outputStream.write( xmlString.getBytes()); - outputStream.close(); - } - else returnErrorXML( result, null, outputStream ); - } - - private void executeAbortMultipartUpload( HttpServletRequest request, HttpServletResponse response ) throws IOException - { - String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - int uploadId = -1; + // [B] Look up all the uploaded body parts and related info + try { + MultipartLoadDao uploadDao = new MultipartLoadDao(); + if (null == uploadDao.multipartExits( uploadId )) { + response.setStatus(404); + returnErrorXML( 404, "NotFound", outputStream ); + return; + } - String temp = request.getParameter("uploadId"); - if (null != temp) uploadId = Integer.parseInt( temp ); - - int result = ServiceProvider.getInstance().getS3Engine().freeUploadParts( bucket, uploadId, true ); + // -> another requirement is that only the upload initiator can upload parts + String initiator = uploadDao.getInitiator( uploadId ); + if (null == initiator || !initiator.equals( UserContext.current().getAccessKey())) { + response.setStatus(403); + returnErrorXML( 403, "Forbidden", outputStream ); + return; + } + + parts = uploadDao.getParts( uploadId, 10000, 0 ); + meta = uploadDao.getMeta( uploadId ); + cannedAccess = uploadDao.getCannedAccess( uploadId ); + } + catch( Exception e ) { + logger.error("executeCompleteMultipartUpload failed due to " + e.getMessage(), e); + response.setStatus(500); + returnErrorXML( 500, "InternalError", outputStream ); + return; + } + + + // [C] Parse the given XML body part and perform error checking + OrderedPair match = verifyParts( request.getInputStream(), parts ); + if (200 != match.getFirst().intValue()) { + response.setStatus(match.getFirst().intValue()); + returnErrorXML( match.getFirst().intValue(), match.getSecond(), outputStream ); + return; + } + + + // [D] Ask the engine to create a newly re-constituted object + S3PutObjectInlineRequest engineRequest = new S3PutObjectInlineRequest(); + engineRequest.setBucketName(bucket); + engineRequest.setKey(key); + engineRequest.setMetaEntries(meta); + engineRequest.setCannedAccess(cannedAccess); + + S3PutObjectInlineResponse engineResponse = ServiceProvider.getInstance().getS3Engine().concatentateMultipartUploads( response, engineRequest, parts, outputStream ); + int result = engineResponse.getResultCode(); + // -> free all multipart state since we now have one concatentated object + if (200 == result) ServiceProvider.getInstance().getS3Engine().freeUploadParts( bucket, uploadId, false ); + + // If all successful then clean up all left over parts + // Notice that "" has already been written into the servlet output stream at the beginning of section [A] + if ( 200 == result ) + { + StringBuffer xml = new StringBuffer(); + xml.append( "" ); + xml.append( "" ).append( "http://" + bucket + ".s3.amazonaws.com/" + key ).append( "" ); + xml.append( "" ).append( bucket ).append( "" ); + xml.append( "" ).append( key ).append( "" ); + xml.append( "\"" ).append( engineResponse.getETag()).append( "\"" ); + xml.append( "" ); + String xmlString = xml.toString().replaceAll("^\\s+", ""); // Remove leading whitespace characters + outputStream.write( xmlString.getBytes()); + outputStream.close(); + } + else returnErrorXML( result, null, outputStream ); + } + + private void executeAbortMultipartUpload( HttpServletRequest request, HttpServletResponse response ) throws IOException + { + String bucket = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + int uploadId = -1; + + String temp = request.getParameter("uploadId"); + if (null != temp) uploadId = Integer.parseInt( temp ); + + int result = ServiceProvider.getInstance().getS3Engine().freeUploadParts( bucket, uploadId, true ); response.setStatus( result ); - } - - private void executeListUploadParts( HttpServletRequest request, HttpServletResponse response ) throws IOException - { - String bucketName = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); - String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); - String owner = null; - String initiator = null; - S3MultipartPart[] parts = null; - int remaining = 0; - int uploadId = -1; - int maxParts = 1000; - int partMarker = 0; - int nextMarker = 0; + } - String temp = request.getParameter("uploadId"); - if (null != temp) uploadId = Integer.parseInt( temp ); + private void executeListUploadParts( HttpServletRequest request, HttpServletResponse response ) throws IOException + { + String bucketName = (String) request.getAttribute(S3Constants.BUCKET_ATTR_KEY); + String key = (String) request.getAttribute(S3Constants.OBJECT_ATTR_KEY); + String owner = null; + String initiator = null; + S3MultipartPart[] parts = null; + int remaining = 0; + int uploadId = -1; + int maxParts = 1000; + int partMarker = 0; + int nextMarker = 0; - temp = request.getParameter("max-parts"); - if (null != temp) { - maxParts = Integer.parseInt( temp ); - if (maxParts > 1000 || maxParts < 0) maxParts = 1000; - } + String temp = request.getParameter("uploadId"); + if (null != temp) uploadId = Integer.parseInt( temp ); - temp = request.getParameter("part-number-marker"); - if (null != temp) partMarker = Integer.parseInt( temp ); + temp = request.getParameter("max-parts"); + if (null != temp) { + maxParts = Integer.parseInt( temp ); + if (maxParts > 1000 || maxParts < 0) maxParts = 1000; + } - - // -> does the bucket exist, we may need it to verify access permissions - SBucketVO bucket = bucketDao.getByName(bucketName); - if (bucket == null) { - logger.error( "listUploadParts failed since " + bucketName + " does not exist" ); - response.setStatus(404); - return; - } - - try { - MultipartLoadDao uploadDao = new MultipartLoadDao(); - OrderedPair exists = uploadDao.multipartExits( uploadId ); - if (null == exists) { - response.setStatus(404); - return; - } - owner = exists.getFirst(); - - // -> the multipart initiator or bucket owner can do this action - initiator = uploadDao.getInitiator( uploadId ); - if (null == initiator || !initiator.equals( UserContext.current().getAccessKey())) - { - try { - // -> write permission on a bucket allows a PutObject / DeleteObject action on any object in the bucket - S3PolicyContext context = new S3PolicyContext( PolicyActions.ListMultipartUploadParts, bucketName ); - context.setKeyName( exists.getSecond()); - S3Engine.verifyAccess( context, "SBucket", bucket.getId(), SAcl.PERMISSION_WRITE ); - } - catch (PermissionDeniedException e) { - response.setStatus(403); - return; - } - } - - parts = uploadDao.getParts( uploadId, maxParts, partMarker ); - remaining = uploadDao.numParts( uploadId, partMarker+maxParts ); - } - catch( Exception e ) { - logger.error("List Uploads failed due to " + e.getMessage(), e); - response.setStatus(500); - } + temp = request.getParameter("part-number-marker"); + if (null != temp) partMarker = Integer.parseInt( temp ); - - StringBuffer xml = new StringBuffer(); - xml.append( "" ); - xml.append( "" ); - xml.append( "" ).append( bucket ).append( "" ); - xml.append( "" ).append( key ).append( "" ); - xml.append( "" ).append( uploadId ).append( "" ); - - // -> currently we just have the access key and have no notion of a display name - xml.append( "" ); - xml.append( "" ).append( initiator ).append( "" ); - xml.append( "" ); - xml.append( "" ); - xml.append( "" ); - xml.append( "" ).append( owner ).append( "" ); - xml.append( "" ); - xml.append( "" ); - - StringBuffer partsList = new StringBuffer(); - for( int i=0; i < parts.length; i++ ) - { - S3MultipartPart onePart = parts[i]; - if (null == onePart) break; - - nextMarker = onePart.getPartNumber(); - partsList.append( "" ); - partsList.append( "" ).append( nextMarker ).append( "" ); - partsList.append( "" ).append( DatatypeConverter.printDateTime( onePart.getLastModified())).append( "" ); - partsList.append( "\"" ).append( onePart.getETag()).append( "\"" ); - partsList.append( "" ).append( onePart.getSize()).append( "" ); - partsList.append( "" ); - } - - xml.append( "STANDARD" ); - xml.append( "" ).append( partMarker ).append( "" ); - xml.append( "" ).append( nextMarker ).append( "" ); - xml.append( "" ).append( maxParts ).append( "" ); - xml.append( "" ).append((0 < remaining ? "true" : "false" )).append( "" ); - xml.append( partsList.toString()); - xml.append( "" ); - - response.setStatus(200); - response.setContentType("text/xml; charset=UTF-8"); - S3RestServlet.endResponse(response, xml.toString()); - } - - /** - * Support the "Range: bytes=0-399" header with just one byte range. - * @param request - * @param engineRequest - * @return - */ - private S3GetObjectRequest setRequestByteRange( HttpServletRequest request, S3GetObjectRequest engineRequest ) - { - String temp = request.getHeader( "Range" ); - if (null == temp) return engineRequest; - - int offset = temp.indexOf( "=" ); - if (-1 != offset) - { - String range = temp.substring( offset+1 ); - - String[] parts = range.split( "-" ); - if (2 >= parts.length) { - // -> the end byte is inclusive - engineRequest.setByteRangeStart( Long.parseLong(parts[0])); - engineRequest.setByteRangeEnd( Long.parseLong(parts[1])+1); - } - } - return engineRequest; - } - - private S3ConditionalHeaders conditionalRequest( HttpServletRequest request, boolean isCopy ) - { - S3ConditionalHeaders headers = new S3ConditionalHeaders(); - - if (isCopy) { - headers.setModifiedSince( request.getHeader( "x-amz-copy-source-if-modified-since" )); - headers.setUnModifiedSince( request.getHeader( "x-amz-copy-source-if-unmodified-since" )); - headers.setMatch( request.getHeader( "x-amz-copy-source-if-match" )); - headers.setNoneMatch( request.getHeader( "x-amz-copy-source-if-none-match" )); - } - else { - headers.setModifiedSince( request.getHeader( "If-Modified-Since" )); - headers.setUnModifiedSince( request.getHeader( "If-Unmodified-Since" )); - headers.setMatch( request.getHeader( "If-Match" )); - headers.setNoneMatch( request.getHeader( "If-None-Match" )); - } + // -> does the bucket exist, we may need it to verify access permissions + SBucketVO bucket = bucketDao.getByName(bucketName); + if (bucket == null) { + logger.error( "listUploadParts failed since " + bucketName + " does not exist" ); + response.setStatus(404); + return; + } + + try { + MultipartLoadDao uploadDao = new MultipartLoadDao(); + OrderedPair exists = uploadDao.multipartExits( uploadId ); + if (null == exists) { + response.setStatus(404); + return; + } + owner = exists.getFirst(); + + // -> the multipart initiator or bucket owner can do this action + initiator = uploadDao.getInitiator( uploadId ); + if (null == initiator || !initiator.equals( UserContext.current().getAccessKey())) + { + try { + // -> write permission on a bucket allows a PutObject / DeleteObject action on any object in the bucket + S3PolicyContext context = new S3PolicyContext( PolicyActions.ListMultipartUploadParts, bucketName ); + context.setKeyName( exists.getSecond()); + S3Engine.verifyAccess( context, "SBucket", bucket.getId(), SAcl.PERMISSION_WRITE ); + } + catch (PermissionDeniedException e) { + response.setStatus(403); + return; + } + } + + parts = uploadDao.getParts( uploadId, maxParts, partMarker ); + remaining = uploadDao.numParts( uploadId, partMarker+maxParts ); + } + catch( Exception e ) { + logger.error("List Uploads failed due to " + e.getMessage(), e); + response.setStatus(500); + } + + + StringBuffer xml = new StringBuffer(); + xml.append( "" ); + xml.append( "" ); + xml.append( "" ).append( bucket ).append( "" ); + xml.append( "" ).append( key ).append( "" ); + xml.append( "" ).append( uploadId ).append( "" ); + + // -> currently we just have the access key and have no notion of a display name + xml.append( "" ); + xml.append( "" ).append( initiator ).append( "" ); + xml.append( "" ); + xml.append( "" ); + xml.append( "" ); + xml.append( "" ).append( owner ).append( "" ); + xml.append( "" ); + xml.append( "" ); + + StringBuffer partsList = new StringBuffer(); + for( int i=0; i < parts.length; i++ ) + { + S3MultipartPart onePart = parts[i]; + if (null == onePart) break; + + nextMarker = onePart.getPartNumber(); + partsList.append( "" ); + partsList.append( "" ).append( nextMarker ).append( "" ); + partsList.append( "" ).append( DatatypeConverter.printDateTime( onePart.getLastModified())).append( "" ); + partsList.append( "\"" ).append( onePart.getETag()).append( "\"" ); + partsList.append( "" ).append( onePart.getSize()).append( "" ); + partsList.append( "" ); + } + + xml.append( "STANDARD" ); + xml.append( "" ).append( partMarker ).append( "" ); + xml.append( "" ).append( nextMarker ).append( "" ); + xml.append( "" ).append( maxParts ).append( "" ); + xml.append( "" ).append((0 < remaining ? "true" : "false" )).append( "" ); + + xml.append( partsList.toString()); + xml.append( "" ); + + response.setStatus(200); + response.setContentType("text/xml; charset=UTF-8"); + S3RestServlet.endResponse(response, xml.toString()); + } + + /** + * Support the "Range: bytes=0-399" header with just one byte range. + * @param request + * @param engineRequest + * @return + */ + private S3GetObjectRequest setRequestByteRange( HttpServletRequest request, S3GetObjectRequest engineRequest ) + { + String temp = request.getHeader( "Range" ); + if (null == temp) return engineRequest; + + int offset = temp.indexOf( "=" ); + if (-1 != offset) + { + String range = temp.substring( offset+1 ); + + String[] parts = range.split( "-" ); + if (2 >= parts.length) { + // -> the end byte is inclusive + engineRequest.setByteRangeStart( Long.parseLong(parts[0])); + engineRequest.setByteRangeEnd( Long.parseLong(parts[1])+1); + } + } + return engineRequest; + } + + private S3ConditionalHeaders conditionalRequest( HttpServletRequest request, boolean isCopy ) + { + S3ConditionalHeaders headers = new S3ConditionalHeaders(); + + if (isCopy) { + headers.setModifiedSince( request.getHeader( "x-amz-copy-source-if-modified-since" )); + headers.setUnModifiedSince( request.getHeader( "x-amz-copy-source-if-unmodified-since" )); + headers.setMatch( request.getHeader( "x-amz-copy-source-if-match" )); + headers.setNoneMatch( request.getHeader( "x-amz-copy-source-if-none-match" )); + } + else { + headers.setModifiedSince( request.getHeader( "If-Modified-Since" )); + headers.setUnModifiedSince( request.getHeader( "If-Unmodified-Since" )); + headers.setMatch( request.getHeader( "If-Match" )); + headers.setNoneMatch( request.getHeader( "If-None-Match" )); + } return headers; - } - - private boolean conditionPassed( HttpServletRequest request, HttpServletResponse response, Date lastModified, String ETag ) - { - S3ConditionalHeaders ifCond = conditionalRequest( request, false ); - - if (0 > ifCond.ifModifiedSince( lastModified )) { - response.setStatus( 304 ); - return false; - } - if (0 > ifCond.ifUnmodifiedSince( lastModified )) { - response.setStatus( 412 ); - return false; - } - if (0 > ifCond.ifMatchEtag( ETag )) { - response.setStatus( 412 ); - return false; - } - if (0 > ifCond.ifNoneMatchEtag( ETag )) { - response.setStatus( 412 ); - return false; - } - return true; - } - - /** - * Return the saved object's meta data back to the client as HTTP "x-amz-meta-" headers. - * This function is constructing an HTTP header and these headers have a defined syntax - * as defined in rfc2616. Any characters that could cause an invalid HTTP header will - * prevent that meta data from being returned via the REST call (as is defined in the Amazon - * spec). These characters can be defined if using the SOAP API as well as the REST API. - * - * @param engineResponse - * @param response - */ - private void returnMetaData( S3GetObjectResponse engineResponse, HttpServletResponse response ) - { - boolean ignoreMeta = false; - int ignoredCount = 0; - - S3MetaDataEntry[] metaSet = engineResponse.getMetaEntries(); - for( int i=0; null != metaSet && i < metaSet.length; i++ ) - { - String name = metaSet[i].getName(); - String value = metaSet[i].getValue(); - byte[] nameBytes = name.getBytes(); - ignoreMeta = false; - - // -> cannot have control characters (octets 0 - 31) and DEL (127), in an HTTP header - for( int j=0; j < name.length(); j++ ) { - if ((0 <= nameBytes[j] && 31 >= nameBytes[j]) || 127 == nameBytes[j]) { - ignoreMeta = true; - break; - } - } - - // -> cannot have HTTP separators in an HTTP header - if (-1 != name.indexOf('(') || -1 != name.indexOf(')') || -1 != name.indexOf('@') || - -1 != name.indexOf('<') || -1 != name.indexOf('>') || -1 != name.indexOf('\"') || - -1 != name.indexOf('[') || -1 != name.indexOf(']') || -1 != name.indexOf('=') || - -1 != name.indexOf(',') || -1 != name.indexOf(';') || -1 != name.indexOf(':') || - -1 != name.indexOf('\\') || -1 != name.indexOf('/') || -1 != name.indexOf(' ') || - -1 != name.indexOf('{') || -1 != name.indexOf('}') || -1 != name.indexOf('?') || - -1 != name.indexOf('\t') - ) ignoreMeta = true; - - - if ( ignoreMeta ) - ignoredCount++; - else response.addHeader( "x-amz-meta-" + name, value ); - } - - if (0 < ignoredCount) response.addHeader( "x-amz-missing-meta", new String( "" + ignoredCount )); - } + } - /** - * Extract the name and value of all meta data so it can be written with the - * object that is being 'PUT'. - * - * @param request - * @return - */ - private S3MetaDataEntry[] extractMetaData( HttpServletRequest request ) - { - List metaSet = new ArrayList(); - int count = 0; - - Enumeration headers = request.getHeaderNames(); + private boolean conditionPassed( HttpServletRequest request, HttpServletResponse response, Date lastModified, String ETag ) + { + S3ConditionalHeaders ifCond = conditionalRequest( request, false ); + + if (0 > ifCond.ifModifiedSince( lastModified )) { + response.setStatus( 304 ); + return false; + } + if (0 > ifCond.ifUnmodifiedSince( lastModified )) { + response.setStatus( 412 ); + return false; + } + if (0 > ifCond.ifMatchEtag( ETag )) { + response.setStatus( 412 ); + return false; + } + if (0 > ifCond.ifNoneMatchEtag( ETag )) { + response.setStatus( 412 ); + return false; + } + return true; + } + + /** + * Return the saved object's meta data back to the client as HTTP "x-amz-meta-" headers. + * This function is constructing an HTTP header and these headers have a defined syntax + * as defined in rfc2616. Any characters that could cause an invalid HTTP header will + * prevent that meta data from being returned via the REST call (as is defined in the Amazon + * spec). These characters can be defined if using the SOAP API as well as the REST API. + * + * @param engineResponse + * @param response + */ + private void returnMetaData( S3GetObjectResponse engineResponse, HttpServletResponse response ) + { + boolean ignoreMeta = false; + int ignoredCount = 0; + + S3MetaDataEntry[] metaSet = engineResponse.getMetaEntries(); + for( int i=0; null != metaSet && i < metaSet.length; i++ ) + { + String name = metaSet[i].getName(); + String value = metaSet[i].getValue(); + byte[] nameBytes = name.getBytes(); + ignoreMeta = false; + + // -> cannot have control characters (octets 0 - 31) and DEL (127), in an HTTP header + for( int j=0; j < name.length(); j++ ) { + if ((0 <= nameBytes[j] && 31 >= nameBytes[j]) || 127 == nameBytes[j]) { + ignoreMeta = true; + break; + } + } + + // -> cannot have HTTP separators in an HTTP header + if (-1 != name.indexOf('(') || -1 != name.indexOf(')') || -1 != name.indexOf('@') || + -1 != name.indexOf('<') || -1 != name.indexOf('>') || -1 != name.indexOf('\"') || + -1 != name.indexOf('[') || -1 != name.indexOf(']') || -1 != name.indexOf('=') || + -1 != name.indexOf(',') || -1 != name.indexOf(';') || -1 != name.indexOf(':') || + -1 != name.indexOf('\\') || -1 != name.indexOf('/') || -1 != name.indexOf(' ') || + -1 != name.indexOf('{') || -1 != name.indexOf('}') || -1 != name.indexOf('?') || + -1 != name.indexOf('\t') + ) ignoreMeta = true; + + + if ( ignoreMeta ) + ignoredCount++; + else response.addHeader( "x-amz-meta-" + name, value ); + } + + if (0 < ignoredCount) response.addHeader( "x-amz-missing-meta", new String( "" + ignoredCount )); + } + + /** + * Extract the name and value of all meta data so it can be written with the + * object that is being 'PUT'. + * + * @param request + * @return + */ + private S3MetaDataEntry[] extractMetaData( HttpServletRequest request ) + { + List metaSet = new ArrayList(); + int count = 0; + + Enumeration headers = request.getHeaderNames(); while( headers.hasMoreElements()) { - String key = (String)headers.nextElement(); - if (key.startsWith( "x-amz-meta-" )) - { - String name = key.substring( 11 ); - String value = request.getHeader( key ); - if (null != value) { - S3MetaDataEntry oneMeta = new S3MetaDataEntry(); - oneMeta.setName( name ); - oneMeta.setValue( value ); - metaSet.add( oneMeta ); - count++; - } - } + String key = (String)headers.nextElement(); + if (key.startsWith( "x-amz-meta-" )) + { + String name = key.substring( 11 ); + String value = request.getHeader( key ); + if (null != value) { + S3MetaDataEntry oneMeta = new S3MetaDataEntry(); + oneMeta.setName( name ); + oneMeta.setValue( value ); + metaSet.add( oneMeta ); + count++; + } + } } if ( 0 < count ) - return metaSet.toArray(new S3MetaDataEntry[0]); + return metaSet.toArray(new S3MetaDataEntry[0]); else return null; - } - - /** - * Parameters on the query string may or may not be name-value pairs. - * For example: "?acl&versionId=2", notice that "acl" has no value other - * than it is present. - * - * @param queryString - from a URL to locate the 'find' parameter - * @param find - name string to return first found - * @return the value matching the found name - */ - private String returnParameter( String queryString, String find ) - { - int offset = queryString.indexOf( find ); - if (-1 != offset) - { - String temp = queryString.substring( offset ); - String[] paramList = temp.split( "[&=]" ); + } + + /** + * Parameters on the query string may or may not be name-value pairs. + * For example: "?acl&versionId=2", notice that "acl" has no value other + * than it is present. + * + * @param queryString - from a URL to locate the 'find' parameter + * @param find - name string to return first found + * @return the value matching the found name + */ + private String returnParameter( String queryString, String find ) + { + int offset = queryString.indexOf( find ); + if (-1 != offset) + { + String temp = queryString.substring( offset ); + String[] paramList = temp.split( "[&=]" ); if (null != paramList && 2 <= paramList.length) return paramList[1]; - } - return null; - } - - private void returnErrorXML( int errorCode, String errorDescription, OutputStream os ) throws IOException - { - StringBuffer xml = new StringBuffer(); - - xml.append( "" ); + } + return null; + } + + private void returnErrorXML( int errorCode, String errorDescription, OutputStream os ) throws IOException + { + StringBuffer xml = new StringBuffer(); + + xml.append( "" ); xml.append( "" ); - + if ( null != errorDescription ) - xml.append( "" ).append( errorDescription ).append( "" ); + xml.append( "" ).append( errorDescription ).append( "" ); else xml.append( "" ).append( errorCode ).append( "" ); - + xml.append( "" ).append( "" ).append( "" ); xml.append( "" ).append( "" ).append( "" ); xml.append( "" ).append( "" ).append( "" ); xml.append( "" ); - + os.write( xml.toString().getBytes()); os.close(); - } - - /** - * The Complete Multipart Upload function pass in the request body a list of - * all uploaded body parts. It is required that we verify that list matches - * what was uploaded. - * - * @param is - * @param parts - * @return error code, and error string - * @throws ParserConfigurationException, IOException, SAXException - */ + } + + /** + * The Complete Multipart Upload function pass in the request body a list of + * all uploaded body parts. It is required that we verify that list matches + * what was uploaded. + * + * @param is + * @param parts + * @return error code, and error string + * @throws ParserConfigurationException, IOException, SAXException + */ private OrderedPair verifyParts( InputStream is, S3MultipartPart[] parts ) { - try { - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware( true ); - - DocumentBuilder db = dbf.newDocumentBuilder(); - Document doc = db.parse( is ); - Node parent = null; - Node contents = null; - NodeList children = null; - String temp = null; - String element = null; - String eTag = null; - int lastNumber = -1; - int partNumber = -1; - int count = 0; + try { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware( true ); - // -> handle with and without a namespace - NodeList nodeSet = doc.getElementsByTagNameNS( "http://s3.amazonaws.com/doc/2006-03-01/", "Part" ); - count = nodeSet.getLength(); - if (0 == count) { - nodeSet = doc.getElementsByTagName( "Part" ); - count = nodeSet.getLength(); - } - if (count != parts.length) return new OrderedPair(400, "InvalidPart"); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse( is ); + Node parent = null; + Node contents = null; + NodeList children = null; + String temp = null; + String element = null; + String eTag = null; + int lastNumber = -1; + int partNumber = -1; + int count = 0; - // -> get a list of all the children elements of the 'Part' parent element - for( int i=0; i < count; i++ ) - { - partNumber = -1; - eTag = null; - parent = nodeSet.item(i); - - if (null != (children = parent.getChildNodes())) - { - int numChildren = children.getLength(); - for( int j=0; j < numChildren; j++ ) - { - contents = children.item( j ); - element = contents.getNodeName().trim(); - if ( element.endsWith( "PartNumber" )) - { - temp = contents.getFirstChild().getNodeValue(); - if (null != temp) partNumber = Integer.parseInt( temp ); - //System.out.println( "part: " + partNumber ); - } - else if (element.endsWith( "ETag" )) - { - eTag = contents.getFirstChild().getNodeValue(); - //System.out.println( "etag: " + eTag ); - } - } - } - - // -> do the parts given in the call XML match what was previously uploaded? - if (lastNumber >= partNumber) { - return new OrderedPair(400, "InvalidPartOrder"); - } - if (partNumber != parts[i].getPartNumber() || - eTag == null || - !eTag.equalsIgnoreCase( "\"" + parts[i].getETag() + "\"" )) { - return new OrderedPair(400, "InvalidPart"); - } - - lastNumber = partNumber; - } - return new OrderedPair(200, "Success"); - } - catch( Exception e ) { - return new OrderedPair(500, e.toString()); - } + // -> handle with and without a namespace + NodeList nodeSet = doc.getElementsByTagNameNS( "http://s3.amazonaws.com/doc/2006-03-01/", "Part" ); + count = nodeSet.getLength(); + if (0 == count) { + nodeSet = doc.getElementsByTagName( "Part" ); + count = nodeSet.getLength(); + } + if (count != parts.length) return new OrderedPair(400, "InvalidPart"); + + // -> get a list of all the children elements of the 'Part' parent element + for( int i=0; i < count; i++ ) + { + partNumber = -1; + eTag = null; + parent = nodeSet.item(i); + + if (null != (children = parent.getChildNodes())) + { + int numChildren = children.getLength(); + for( int j=0; j < numChildren; j++ ) + { + contents = children.item( j ); + element = contents.getNodeName().trim(); + if ( element.endsWith( "PartNumber" )) + { + temp = contents.getFirstChild().getNodeValue(); + if (null != temp) partNumber = Integer.parseInt( temp ); + //System.out.println( "part: " + partNumber ); + } + else if (element.endsWith( "ETag" )) + { + eTag = contents.getFirstChild().getNodeValue(); + //System.out.println( "etag: " + eTag ); + } + } + } + + // -> do the parts given in the call XML match what was previously uploaded? + if (lastNumber >= partNumber) { + return new OrderedPair(400, "InvalidPartOrder"); + } + if (partNumber != parts[i].getPartNumber() || + eTag == null || + !eTag.equalsIgnoreCase( "\"" + parts[i].getETag() + "\"" )) { + return new OrderedPair(400, "InvalidPart"); + } + + lastNumber = partNumber; + } + return new OrderedPair(200, "Success"); + } + catch( Exception e ) { + return new OrderedPair(500, e.toString()); + } } } diff --git a/awsapi/src/com/cloud/bridge/service/controller/s3/ServiceProvider.java b/awsapi/src/com/cloud/bridge/service/controller/s3/ServiceProvider.java index 2ddbbf2a57d..4eb4c3b99a1 100644 --- a/awsapi/src/com/cloud/bridge/service/controller/s3/ServiceProvider.java +++ b/awsapi/src/com/cloud/bridge/service/controller/s3/ServiceProvider.java @@ -25,29 +25,27 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.InetAddress; -import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Timer; import java.util.TimerTask; +import javax.inject.Inject; + import org.apache.axis2.AxisFault; import org.apache.log4j.Logger; import org.apache.log4j.xml.DOMConfigurator; -import com.amazon.s3.AmazonS3SkeletonInterface; import com.amazon.ec2.AmazonEC2SkeletonInterface; +import com.amazon.s3.AmazonS3SkeletonInterface; import com.cloud.bridge.model.MHostVO; import com.cloud.bridge.model.SHost; import com.cloud.bridge.model.SHostVO; import com.cloud.bridge.model.UserCredentialsVO; import com.cloud.bridge.persist.dao.MHostDao; -import com.cloud.bridge.persist.dao.MHostDaoImpl; import com.cloud.bridge.persist.dao.SHostDao; -import com.cloud.bridge.persist.dao.SHostDaoImpl; import com.cloud.bridge.persist.dao.UserCredentialsDao; -import com.cloud.bridge.persist.dao.UserCredentialsDaoImpl; import com.cloud.bridge.service.EC2SoapServiceImpl; import com.cloud.bridge.service.UserInfo; import com.cloud.bridge.service.core.ec2.EC2Engine; @@ -58,191 +56,190 @@ import com.cloud.bridge.util.ConfigurationHelper; import com.cloud.bridge.util.DateHelper; import com.cloud.bridge.util.NetHelper; import com.cloud.bridge.util.OrderedPair; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; public class ServiceProvider { - protected final static Logger logger = Logger.getLogger(ServiceProvider.class); - protected final MHostDao mhostDao = ComponentLocator.inject(MHostDaoImpl.class); - protected final SHostDao shostDao = ComponentLocator.inject(SHostDaoImpl.class); - protected final UserCredentialsDao ucDao = ComponentLocator.inject(UserCredentialsDaoImpl.class); - - public final static long HEARTBEAT_INTERVAL = 10000; + protected final static Logger logger = Logger.getLogger(ServiceProvider.class); + @Inject MHostDao mhostDao; + @Inject SHostDao shostDao; + @Inject UserCredentialsDao ucDao; - private static ServiceProvider instance; + public final static long HEARTBEAT_INTERVAL = 10000; - private Map, Object> serviceMap = new HashMap, Object>(); - private Timer timer = new Timer(); - private MHostVO mhost; - private Properties properties; - private boolean useSubDomain = false; // use DNS sub domain for bucket name - private String serviceEndpoint = null; - private String multipartDir = null; // illegal bucket name used as a folder for storing multiparts - private String masterDomain = ".s3.amazonaws.com"; - private S3Engine engine; - private EC2Engine EC2_engine = null; + private static ServiceProvider instance; - // -> cache Bucket Policies here so we don't have to load from db on every access - private Map policyMap = new HashMap(); + private final Map, Object> serviceMap = new HashMap, Object>(); + private final Timer timer = new Timer(); + private MHostVO mhost; + private Properties properties; + private boolean useSubDomain = false; // use DNS sub domain for bucket name + private String serviceEndpoint = null; + private String multipartDir = null; // illegal bucket name used as a folder for storing multiparts + private String masterDomain = ".s3.amazonaws.com"; + private final S3Engine engine; + private EC2Engine EC2_engine = null; - protected ServiceProvider() throws IOException { - // register service implementation object - Transaction txn = Transaction.open(Transaction.AWSAPI_DB); - txn.close(); - engine = new S3Engine(); - EC2_engine = new EC2Engine(); - serviceMap.put(AmazonS3SkeletonInterface.class, new S3SerializableServiceImplementation(engine)); - serviceMap.put(AmazonEC2SkeletonInterface.class, new EC2SoapServiceImpl(EC2_engine)); - } + // -> cache Bucket Policies here so we don't have to load from db on every access + private final Map policyMap = new HashMap(); - public synchronized static ServiceProvider getInstance() { - if(instance == null) - { - try { - instance = new ServiceProvider(); - instance.initialize(); - } catch(Throwable e) { - logger.error("Unexpected exception " + e.getMessage(), e); - } finally { - } - } - return instance; - } + protected ServiceProvider() throws IOException { + // register service implementation object + Transaction txn = Transaction.open(Transaction.AWSAPI_DB); + txn.close(); + engine = new S3Engine(); + EC2_engine = new EC2Engine(); + serviceMap.put(AmazonS3SkeletonInterface.class, new S3SerializableServiceImplementation(engine)); + serviceMap.put(AmazonEC2SkeletonInterface.class, new EC2SoapServiceImpl(EC2_engine)); + } - public long getManagementHostId() { - // we want to limit mhost within its own session, id of the value will be returned - long mhostId = 0; - if(mhost != null) - mhostId = mhost.getId() != null ? mhost.getId().longValue() : 0L; - return mhostId; - } + public synchronized static ServiceProvider getInstance() { + if(instance == null) + { + try { + instance = new ServiceProvider(); + instance.initialize(); + } catch(Throwable e) { + logger.error("Unexpected exception " + e.getMessage(), e); + } finally { + } + } + return instance; + } - /** - * We return a 2-tuple to distinguish between two cases: - * (1) there is no entry in the map for bucketName, and (2) there is a null entry - * in the map for bucketName. In case 2, the database was inspected for the - * bucket policy but it had none so we cache it here to reduce database lookups. - * @param bucketName - * @return Integer in the tuple means: -1 if no policy defined for the bucket, 0 if one defined - * even if it is set at null. - */ - public OrderedPair getBucketPolicy(String bucketName) { + public long getManagementHostId() { + // we want to limit mhost within its own session, id of the value will be returned + long mhostId = 0; + if(mhost != null) + mhostId = mhost.getId() != null ? mhost.getId().longValue() : 0L; + return mhostId; + } - if (policyMap.containsKey( bucketName )) { - S3BucketPolicy policy = policyMap.get( bucketName ); - return new OrderedPair( policy, 0 ); - } - else return new OrderedPair( null, -1 ); // For case (1) where the map has no entry for bucketName - } + /** + * We return a 2-tuple to distinguish between two cases: + * (1) there is no entry in the map for bucketName, and (2) there is a null entry + * in the map for bucketName. In case 2, the database was inspected for the + * bucket policy but it had none so we cache it here to reduce database lookups. + * @param bucketName + * @return Integer in the tuple means: -1 if no policy defined for the bucket, 0 if one defined + * even if it is set at null. + */ + public OrderedPair getBucketPolicy(String bucketName) { - /** - * The policy parameter can be set to null, which means that there is no policy - * for the bucket so a database lookup is not necessary. - * - * @param bucketName - * @param policy - */ - public void setBucketPolicy(String bucketName, S3BucketPolicy policy) { - policyMap.put(bucketName, policy); - } + if (policyMap.containsKey( bucketName )) { + S3BucketPolicy policy = policyMap.get( bucketName ); + return new OrderedPair( policy, 0 ); + } + else return new OrderedPair( null, -1 ); // For case (1) where the map has no entry for bucketName + } - public void deleteBucketPolicy(String bucketName) { - policyMap.remove(bucketName); - } + /** + * The policy parameter can be set to null, which means that there is no policy + * for the bucket so a database lookup is not necessary. + * + * @param bucketName + * @param policy + */ + public void setBucketPolicy(String bucketName, S3BucketPolicy policy) { + policyMap.put(bucketName, policy); + } - public S3Engine getS3Engine() { - return engine; - } + public void deleteBucketPolicy(String bucketName) { + policyMap.remove(bucketName); + } - public EC2Engine getEC2Engine() { - return EC2_engine; - } + public S3Engine getS3Engine() { + return engine; + } - public String getMasterDomain() { - return masterDomain; - } + public EC2Engine getEC2Engine() { + return EC2_engine; + } - public boolean getUseSubDomain() { - return useSubDomain; - } + public String getMasterDomain() { + return masterDomain; + } - public String getServiceEndpoint() { - return serviceEndpoint; - } + public boolean getUseSubDomain() { + return useSubDomain; + } - public String getMultipartDir() { - return multipartDir; - } + public String getServiceEndpoint() { + return serviceEndpoint; + } - public Properties getStartupProperties() { - return properties; - } + public String getMultipartDir() { + return multipartDir; + } - public UserInfo getUserInfo(String accessKey) { - UserInfo info = new UserInfo(); - Transaction txn = Transaction.open(Transaction.AWSAPI_DB); - try { - txn.start(); - UserCredentialsVO cloudKeys = ucDao.getByAccessKey( accessKey ); - if ( null == cloudKeys ) { - logger.debug( accessKey + " is not defined in the S3 service - call SetUserKeys" ); - return null; - } else { - info.setAccessKey( accessKey ); - info.setSecretKey( cloudKeys.getSecretKey()); - info.setCanonicalUserId(accessKey); - info.setDescription( "S3 REST request" ); - return info; - } - }finally { - txn.commit(); - } - } - - @DB - protected void initialize() { - if(logger.isInfoEnabled()) - logger.info("Initializing ServiceProvider..."); - - Transaction txn = Transaction.open(Transaction.AWSAPI_DB); - //txn.close(); + public Properties getStartupProperties() { + return properties; + } - File file = ConfigurationHelper.findConfigurationFile("log4j-cloud.xml"); - if(file != null) { - System.out.println("Log4j configuration from : " + file.getAbsolutePath()); - DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000); - } else { - System.out.println("Configure log4j with default properties"); - } + public UserInfo getUserInfo(String accessKey) { + UserInfo info = new UserInfo(); + Transaction txn = Transaction.open(Transaction.AWSAPI_DB); + try { + txn.start(); + UserCredentialsVO cloudKeys = ucDao.getByAccessKey( accessKey ); + if ( null == cloudKeys ) { + logger.debug( accessKey + " is not defined in the S3 service - call SetUserKeys" ); + return null; + } else { + info.setAccessKey( accessKey ); + info.setSecretKey( cloudKeys.getSecretKey()); + info.setCanonicalUserId(accessKey); + info.setDescription( "S3 REST request" ); + return info; + } + }finally { + txn.commit(); + } + } - loadStartupProperties(); - String hostKey = properties.getProperty("host.key"); - if(hostKey == null) { - InetAddress inetAddr = NetHelper.getFirstNonLoopbackLocalInetAddress(); - if(inetAddr != null) - hostKey = NetHelper.getMacAddress(inetAddr); - } - if(hostKey == null) - throw new ConfigurationException("Please configure host.key property in cloud-bridge.properites"); - String host = properties.getProperty("host"); - if(host == null) - host = NetHelper.getHostName(); + @DB + protected void initialize() { + if(logger.isInfoEnabled()) + logger.info("Initializing ServiceProvider..."); - if(properties.get("bucket.dns") != null && - ((String)properties.get("bucket.dns")).equalsIgnoreCase("true")) { - useSubDomain = true; - } + Transaction txn = Transaction.open(Transaction.AWSAPI_DB); + //txn.close(); - serviceEndpoint = (String)properties.get("serviceEndpoint"); - masterDomain = new String( "." + serviceEndpoint ); + File file = ConfigurationHelper.findConfigurationFile("log4j-cloud.xml"); + if(file != null) { + System.out.println("Log4j configuration from : " + file.getAbsolutePath()); + DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000); + } else { + System.out.println("Configure log4j with default properties"); + } - setupHost(hostKey, host); + loadStartupProperties(); + String hostKey = properties.getProperty("host.key"); + if(hostKey == null) { + InetAddress inetAddr = NetHelper.getFirstNonLoopbackLocalInetAddress(); + if(inetAddr != null) + hostKey = NetHelper.getMacAddress(inetAddr); + } + if(hostKey == null) + throw new ConfigurationException("Please configure host.key property in cloud-bridge.properites"); + String host = properties.getProperty("host"); + if(host == null) + host = NetHelper.getHostName(); - // we will commit and start a new transaction to allow host info be flushed to DB - //PersistContext.flush(); + if(properties.get("bucket.dns") != null && + ((String)properties.get("bucket.dns")).equalsIgnoreCase("true")) { + useSubDomain = true; + } - String localStorageRoot = properties.getProperty("storage.root"); + serviceEndpoint = (String)properties.get("serviceEndpoint"); + masterDomain = new String( "." + serviceEndpoint ); + + setupHost(hostKey, host); + + // we will commit and start a new transaction to allow host info be flushed to DB + //PersistContext.flush(); + + String localStorageRoot = properties.getProperty("storage.root"); if (localStorageRoot != null) { if (localStorageRoot.toLowerCase().startsWith("castor")) { setupCAStorStorage(localStorageRoot); @@ -251,138 +248,139 @@ public class ServiceProvider { } } - multipartDir = properties.getProperty("storage.multipartDir"); - - Transaction txn1 = Transaction.open(Transaction.AWSAPI_DB); - timer.schedule(getHeartbeatTask(), HEARTBEAT_INTERVAL, HEARTBEAT_INTERVAL); - txn1.close(); + multipartDir = properties.getProperty("storage.multipartDir"); - if(logger.isInfoEnabled()) - logger.info("ServiceProvider initialized"); - } + Transaction txn1 = Transaction.open(Transaction.AWSAPI_DB); + timer.schedule(getHeartbeatTask(), HEARTBEAT_INTERVAL, HEARTBEAT_INTERVAL); + txn1.close(); - private void loadStartupProperties() { - File propertiesFile = ConfigurationHelper.findConfigurationFile("cloud-bridge.properties"); - properties = new Properties(); - if(propertiesFile != null) { - try { - properties.load(new FileInputStream(propertiesFile)); - } catch (FileNotFoundException e) { - logger.warn("Unable to open properties file: " + propertiesFile.getAbsolutePath(), e); - } catch (IOException e) { - logger.warn("Unable to read properties file: " + propertiesFile.getAbsolutePath(), e); - } - - logger.info("Use startup properties file: " + propertiesFile.getAbsolutePath()); - } else { - if(logger.isInfoEnabled()) - logger.info("Startup properties is not found."); - } - } - - private TimerTask getHeartbeatTask() { - return new TimerTask() { - - @Override - public void run() { - try { - mhost.setLastHeartbeatTime(DateHelper.currentGMTTime()); - mhostDao.updateHeartBeat(mhost); - } catch(Throwable e){ - logger.error("Unexpected exception " + e.getMessage(), e); - } finally { - } - } - }; - } - - private void setupHost(String hostKey, String host) { - - mhost = mhostDao.getByHostKey(hostKey); - if(mhost == null) { - mhost = new MHostVO(); - mhost.setHostKey(hostKey); - mhost.setHost(host); - mhost.setLastHeartbeatTime(DateHelper.currentGMTTime()); - mhost = mhostDao.persist(mhost); - } else { - mhost.setHost(host); - mhostDao.update(mhost.getId(), mhost); - } - } - - private void setupLocalStorage(String storageRoot) { - SHostVO shost = shostDao.getLocalStorageHost(mhost.getId(), storageRoot); - if(shost == null) { - shost = new SHostVO(); - shost.setMhost(mhost); - shost.setMhostid(mhost.getId()); - shost.setHostType(SHost.STORAGE_HOST_TYPE_LOCAL); - shost.setHost(NetHelper.getHostName()); - shost.setExportRoot(storageRoot); - shostDao.persist(shost); - } - } - - private void setupCAStorStorage(String storageRoot) { - SHostVO shost = shostDao.getLocalStorageHost(mhost.getId(), storageRoot); - if(shost == null) { - shost = new SHostVO(); - shost.setMhost(mhost); - shost.setMhostid(mhost.getId()); - shost.setHostType(SHost.STORAGE_HOST_TYPE_CASTOR); - shost.setHost(NetHelper.getHostName()); - shost.setExportRoot(storageRoot); - shostDao.persist(shost); - } + if(logger.isInfoEnabled()) + logger.info("ServiceProvider initialized"); } - public void shutdown() { - timer.cancel(); + private void loadStartupProperties() { + File propertiesFile = ConfigurationHelper.findConfigurationFile("cloud-bridge.properties"); + properties = new Properties(); + if(propertiesFile != null) { + try { + properties.load(new FileInputStream(propertiesFile)); + } catch (FileNotFoundException e) { + logger.warn("Unable to open properties file: " + propertiesFile.getAbsolutePath(), e); + } catch (IOException e) { + logger.warn("Unable to read properties file: " + propertiesFile.getAbsolutePath(), e); + } - if(logger.isInfoEnabled()) - logger.info("ServiceProvider stopped"); - } + logger.info("Use startup properties file: " + propertiesFile.getAbsolutePath()); + } else { + if(logger.isInfoEnabled()) + logger.info("Startup properties is not found."); + } + } - @SuppressWarnings("unchecked") - private static T getProxy(Class serviceInterface, final T serviceObject) { - return (T) Proxy.newProxyInstance(serviceObject.getClass().getClassLoader(), - new Class[] { serviceInterface }, - new InvocationHandler() { - public Object invoke(Object proxy, Method method, - Object[] args) throws Throwable { - Object result = null; - try { - result = method.invoke(serviceObject, args); - } catch (Throwable e) { - // Rethrow the exception to Axis: - // Check if the exception is an AxisFault or a - // RuntimeException - // enveloped AxisFault and if so, pass it on as - // such. Otherwise - // log to help debugging and throw as is. - if (e.getCause() != null - && e.getCause() instanceof AxisFault) - throw e.getCause(); - else if (e.getCause() != null - && e.getCause().getCause() != null - && e.getCause().getCause() instanceof AxisFault) - throw e.getCause().getCause(); - else { - logger.warn( - "Unhandled exception " + e.getMessage(), - e); - throw e; - } - } finally { - } - return result; + private TimerTask getHeartbeatTask() { + return new TimerTask() { + + @Override + public void run() { + try { + mhost.setLastHeartbeatTime(DateHelper.currentGMTTime()); + mhostDao.updateHeartBeat(mhost); + } catch(Throwable e){ + logger.error("Unexpected exception " + e.getMessage(), e); + } finally { + } + } + }; + } + + private void setupHost(String hostKey, String host) { + + mhost = mhostDao.getByHostKey(hostKey); + if(mhost == null) { + mhost = new MHostVO(); + mhost.setHostKey(hostKey); + mhost.setHost(host); + mhost.setLastHeartbeatTime(DateHelper.currentGMTTime()); + mhost = mhostDao.persist(mhost); + } else { + mhost.setHost(host); + mhostDao.update(mhost.getId(), mhost); + } + } + + private void setupLocalStorage(String storageRoot) { + SHostVO shost = shostDao.getLocalStorageHost(mhost.getId(), storageRoot); + if(shost == null) { + shost = new SHostVO(); + shost.setMhost(mhost); + shost.setMhostid(mhost.getId()); + shost.setHostType(SHost.STORAGE_HOST_TYPE_LOCAL); + shost.setHost(NetHelper.getHostName()); + shost.setExportRoot(storageRoot); + shostDao.persist(shost); + } + } + + private void setupCAStorStorage(String storageRoot) { + SHostVO shost = shostDao.getLocalStorageHost(mhost.getId(), storageRoot); + if(shost == null) { + shost = new SHostVO(); + shost.setMhost(mhost); + shost.setMhostid(mhost.getId()); + shost.setHostType(SHost.STORAGE_HOST_TYPE_CASTOR); + shost.setHost(NetHelper.getHostName()); + shost.setExportRoot(storageRoot); + shostDao.persist(shost); + } + } + + public void shutdown() { + timer.cancel(); + + if(logger.isInfoEnabled()) + logger.info("ServiceProvider stopped"); + } + + @SuppressWarnings("unchecked") + private static T getProxy(Class serviceInterface, final T serviceObject) { + return (T) Proxy.newProxyInstance(serviceObject.getClass().getClassLoader(), + new Class[] { serviceInterface }, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, + Object[] args) throws Throwable { + Object result = null; + try { + result = method.invoke(serviceObject, args); + } catch (Throwable e) { + // Rethrow the exception to Axis: + // Check if the exception is an AxisFault or a + // RuntimeException + // enveloped AxisFault and if so, pass it on as + // such. Otherwise + // log to help debugging and throw as is. + if (e.getCause() != null + && e.getCause() instanceof AxisFault) + throw e.getCause(); + else if (e.getCause() != null + && e.getCause().getCause() != null + && e.getCause().getCause() instanceof AxisFault) + throw e.getCause().getCause(); + else { + logger.warn( + "Unhandled exception " + e.getMessage(), + e); + throw e; } - }); - } + } finally { + } + return result; + } + }); + } - @SuppressWarnings("unchecked") - public T getServiceImpl(Class serviceInterface) { - return getProxy(serviceInterface, (T)serviceMap.get(serviceInterface)); - } + @SuppressWarnings("unchecked") + public T getServiceImpl(Class serviceInterface) { + return getProxy(serviceInterface, (T)serviceMap.get(serviceInterface)); + } } diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java index eb25249bd92..0f8eded815f 100644 --- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java @@ -22,9 +22,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.SignatureException; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; import java.text.ParseException; import java.util.ArrayList; @@ -32,6 +29,7 @@ import java.util.List; import java.util.Properties; import java.util.UUID; +import javax.inject.Inject; import javax.xml.parsers.ParserConfigurationException; import org.apache.log4j.Logger; @@ -39,13 +37,9 @@ import org.xml.sax.SAXException; import com.cloud.bridge.model.CloudStackServiceOfferingVO; import com.cloud.bridge.persist.dao.CloudStackAccountDao; -import com.cloud.bridge.persist.dao.CloudStackAccountDaoImpl; import com.cloud.bridge.persist.dao.CloudStackSvcOfferingDao; -import com.cloud.bridge.persist.dao.CloudStackSvcOfferingDaoImpl; -import com.cloud.bridge.persist.dao.OfferingDaoImpl; -import com.cloud.bridge.persist.dao.SObjectItemDaoImpl; +import com.cloud.bridge.persist.dao.OfferingDao; import com.cloud.bridge.service.UserContext; - import com.cloud.bridge.service.core.ec2.EC2ImageAttributes.ImageAttribute; import com.cloud.bridge.service.exception.EC2ServiceException; import com.cloud.bridge.service.exception.EC2ServiceException.ClientError; @@ -68,7 +62,6 @@ import com.cloud.stack.models.CloudStackResourceLimit; import com.cloud.stack.models.CloudStackResourceTag; import com.cloud.stack.models.CloudStackSecurityGroup; import com.cloud.stack.models.CloudStackSecurityGroupIngress; -import com.cloud.stack.models.CloudStackServiceOffering; import com.cloud.stack.models.CloudStackSnapshot; import com.cloud.stack.models.CloudStackTemplate; import com.cloud.stack.models.CloudStackTemplatePermission; @@ -76,453 +69,451 @@ import com.cloud.stack.models.CloudStackUser; import com.cloud.stack.models.CloudStackUserVm; import com.cloud.stack.models.CloudStackVolume; import com.cloud.stack.models.CloudStackZone; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.db.Transaction; /** * EC2Engine processes the ec2 commands and calls their cloudstack analogs * */ public class EC2Engine { - protected final static Logger logger = Logger.getLogger(EC2Engine.class); - String managementServer = null; - String cloudAPIPort = null; + protected final static Logger logger = Logger.getLogger(EC2Engine.class); + String managementServer = null; + String cloudAPIPort = null; - protected final CloudStackSvcOfferingDao scvoDao = ComponentLocator.inject(CloudStackSvcOfferingDaoImpl.class); - protected final OfferingDaoImpl ofDao = ComponentLocator.inject(OfferingDaoImpl.class); - CloudStackAccountDao accDao = ComponentLocator.inject(CloudStackAccountDaoImpl.class); - private CloudStackApi _eng = null; - - private CloudStackAccount currentAccount = null; + @Inject CloudStackSvcOfferingDao scvoDao; + @Inject OfferingDao ofDao; + @Inject CloudStackAccountDao accDao; + private CloudStackApi _eng = null; - public EC2Engine() throws IOException { - loadConfigValues(); - } + private CloudStackAccount currentAccount = null; - /** - * Which management server to we talk to? - * Load a mapping form Amazon values for 'instanceType' to cloud defined - * diskOfferingId and serviceOfferingId. - * - * @throws IOException - */ - private void loadConfigValues() throws IOException { - File propertiesFile = ConfigurationHelper.findConfigurationFile("ec2-service.properties"); - if (null != propertiesFile) { - logger.info("Use EC2 properties file: " + propertiesFile.getAbsolutePath()); - Properties EC2Prop = new Properties(); - try { - EC2Prop.load( new FileInputStream( propertiesFile )); - } catch (FileNotFoundException e) { - logger.warn("Unable to open properties file: " + propertiesFile.getAbsolutePath(), e); - } catch (IOException e) { - logger.warn("Unable to read properties file: " + propertiesFile.getAbsolutePath(), e); - } - managementServer = EC2Prop.getProperty( "managementServer" ); - cloudAPIPort = EC2Prop.getProperty( "cloudAPIPort", null ); - - try { - if(ofDao.getOfferingCount() == 0) { - String strValue = EC2Prop.getProperty("m1.small.serviceId"); - if(strValue != null) ofDao.setOfferMapping("m1.small", strValue); + public EC2Engine() throws IOException { + loadConfigValues(); + } - strValue = EC2Prop.getProperty("m1.large.serviceId"); - if(strValue != null) ofDao.setOfferMapping("m1.large", strValue); + /** + * Which management server to we talk to? + * Load a mapping form Amazon values for 'instanceType' to cloud defined + * diskOfferingId and serviceOfferingId. + * + * @throws IOException + */ + private void loadConfigValues() throws IOException { + File propertiesFile = ConfigurationHelper.findConfigurationFile("ec2-service.properties"); + if (null != propertiesFile) { + logger.info("Use EC2 properties file: " + propertiesFile.getAbsolutePath()); + Properties EC2Prop = new Properties(); + try { + EC2Prop.load( new FileInputStream( propertiesFile )); + } catch (FileNotFoundException e) { + logger.warn("Unable to open properties file: " + propertiesFile.getAbsolutePath(), e); + } catch (IOException e) { + logger.warn("Unable to read properties file: " + propertiesFile.getAbsolutePath(), e); + } + managementServer = EC2Prop.getProperty( "managementServer" ); + cloudAPIPort = EC2Prop.getProperty( "cloudAPIPort", null ); - strValue = EC2Prop.getProperty("m1.xlarge.serviceId"); - if(strValue != null) ofDao.setOfferMapping("m1.xlarge", strValue); + try { + if(ofDao.getOfferingCount() == 0) { + String strValue = EC2Prop.getProperty("m1.small.serviceId"); + if(strValue != null) ofDao.setOfferMapping("m1.small", strValue); - strValue = EC2Prop.getProperty("c1.medium.serviceId"); - if(strValue != null) ofDao.setOfferMapping("c1.medium", strValue); + strValue = EC2Prop.getProperty("m1.large.serviceId"); + if(strValue != null) ofDao.setOfferMapping("m1.large", strValue); - strValue = EC2Prop.getProperty("c1.xlarge.serviceId"); - if(strValue != null) ofDao.setOfferMapping("c1.xlarge", strValue); + strValue = EC2Prop.getProperty("m1.xlarge.serviceId"); + if(strValue != null) ofDao.setOfferMapping("m1.xlarge", strValue); - strValue = EC2Prop.getProperty("m2.xlarge.serviceId"); - if(strValue != null) ofDao.setOfferMapping("m2.xlarge", strValue); + strValue = EC2Prop.getProperty("c1.medium.serviceId"); + if(strValue != null) ofDao.setOfferMapping("c1.medium", strValue); - strValue = EC2Prop.getProperty("m2.2xlarge.serviceId"); - if(strValue != null) ofDao.setOfferMapping("m2.2xlarge", strValue); + strValue = EC2Prop.getProperty("c1.xlarge.serviceId"); + if(strValue != null) ofDao.setOfferMapping("c1.xlarge", strValue); - strValue = EC2Prop.getProperty("m2.4xlarge.serviceId"); - if(strValue != null) ofDao.setOfferMapping("m2.4xlarge", strValue); + strValue = EC2Prop.getProperty("m2.xlarge.serviceId"); + if(strValue != null) ofDao.setOfferMapping("m2.xlarge", strValue); - strValue = EC2Prop.getProperty("cc1.4xlarge.serviceId"); - if(strValue != null) ofDao.setOfferMapping("cc1.4xlarge", strValue); - } - } catch(Exception e) { - logger.error("Unexpected exception ", e); - } - } else logger.error( "ec2-service.properties not found" ); - } - - /** - * Helper function to manage the api connection - * - * @return - */ - private CloudStackApi getApi() { - if (_eng == null) { - _eng = new CloudStackApi(managementServer, cloudAPIPort, false); - } - // regardless of whether _eng is initialized, we must make sure - // access/secret keys are current with what's in the UserCredentials + strValue = EC2Prop.getProperty("m2.2xlarge.serviceId"); + if(strValue != null) ofDao.setOfferMapping("m2.2xlarge", strValue); + + strValue = EC2Prop.getProperty("m2.4xlarge.serviceId"); + if(strValue != null) ofDao.setOfferMapping("m2.4xlarge", strValue); + + strValue = EC2Prop.getProperty("cc1.4xlarge.serviceId"); + if(strValue != null) ofDao.setOfferMapping("cc1.4xlarge", strValue); + } + } catch(Exception e) { + logger.error("Unexpected exception ", e); + } + } else logger.error( "ec2-service.properties not found" ); + } + + /** + * Helper function to manage the api connection + * + * @return + */ + private CloudStackApi getApi() { + if (_eng == null) { + _eng = new CloudStackApi(managementServer, cloudAPIPort, false); + } + // regardless of whether _eng is initialized, we must make sure + // access/secret keys are current with what's in the UserCredentials _eng.setApiKey(UserContext.current().getAccessKey()); _eng.setSecretKey(UserContext.current().getSecretKey()); - return _eng; - } + return _eng; + } - /** - * Verifies account can access CloudStack - * - * @param accessKey - * @param secretKey - * @return - * @throws EC2ServiceException - */ - public boolean validateAccount( String accessKey, String secretKey ) throws EC2ServiceException { - String oldApiKey = null; - String oldSecretKey = null; + /** + * Verifies account can access CloudStack + * + * @param accessKey + * @param secretKey + * @return + * @throws EC2ServiceException + */ + public boolean validateAccount( String accessKey, String secretKey ) throws EC2ServiceException { + String oldApiKey = null; + String oldSecretKey = null; - if (accessKey == null || secretKey == null) { + if (accessKey == null || secretKey == null) { return false; } - - // okay, instead of using the getApi() nonsense for validate, we are going to manage _eng - if (_eng == null) { + + // okay, instead of using the getApi() nonsense for validate, we are going to manage _eng + if (_eng == null) { _eng = new CloudStackApi(managementServer, cloudAPIPort, false); - } - - try { - oldApiKey = _eng.getApiKey(); - oldSecretKey = _eng.getSecretKey(); - } catch(Exception e) { - // we really don't care, and expect this - } + } + try { - _eng.setApiKey(accessKey); - _eng.setSecretKey(secretKey); - List accts = _eng.listAccounts(null, null, null, null, null, null, null, null); - if (oldApiKey != null && oldSecretKey != null) { - _eng.setApiKey(oldApiKey); - _eng.setSecretKey(oldSecretKey); - } - if (accts == null) { - return false; - } - return true; - } catch(Exception e) { - logger.error("Validate account failed!"); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - } + oldApiKey = _eng.getApiKey(); + oldSecretKey = _eng.getSecretKey(); + } catch(Exception e) { + // we really don't care, and expect this + } + try { + _eng.setApiKey(accessKey); + _eng.setSecretKey(secretKey); + List accts = _eng.listAccounts(null, null, null, null, null, null, null, null); + if (oldApiKey != null && oldSecretKey != null) { + _eng.setApiKey(oldApiKey); + _eng.setSecretKey(oldSecretKey); + } + if (accts == null) { + return false; + } + return true; + } catch(Exception e) { + logger.error("Validate account failed!"); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } - /** - * Creates a security group - * - * @param groupName - * @param groupDesc - * @return - */ - public Boolean createSecurityGroup(String groupName, String groupDesc) { - try { - CloudStackSecurityGroup grp = getApi().createSecurityGroup(groupName, null, groupDesc, null); - if (grp != null && grp.getId() != null) { - return true; - } - return false; - } catch( Exception e ) { - logger.error( "EC2 CreateSecurityGroup - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - } + /** + * Creates a security group + * + * @param groupName + * @param groupDesc + * @return + */ + public Boolean createSecurityGroup(String groupName, String groupDesc) { + try { + CloudStackSecurityGroup grp = getApi().createSecurityGroup(groupName, null, groupDesc, null); + if (grp != null && grp.getId() != null) { + return true; + } + return false; + } catch( Exception e ) { + logger.error( "EC2 CreateSecurityGroup - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } - /** - * Deletes a security group - * - * @param groupName - * @return - */ - public boolean deleteSecurityGroup(String groupName) { - try { - CloudStackInfoResponse resp = getApi().deleteSecurityGroup(null, null, null, groupName); - if (resp != null) { - return resp.getSuccess(); - } - return false; - } catch( Exception e ) { - logger.error( "EC2 DeleteSecurityGroup - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - } + /** + * Deletes a security group + * + * @param groupName + * @return + */ + public boolean deleteSecurityGroup(String groupName) { + try { + CloudStackInfoResponse resp = getApi().deleteSecurityGroup(null, null, null, groupName); + if (resp != null) { + return resp.getSuccess(); + } + return false; + } catch( Exception e ) { + logger.error( "EC2 DeleteSecurityGroup - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } - /** - * returns a list of security groups - * - * @param request - * @return - */ - public EC2DescribeSecurityGroupsResponse describeSecurityGroups(EC2DescribeSecurityGroups request) - { - try { - EC2DescribeSecurityGroupsResponse response = listSecurityGroups( request.getGroupSet()); - EC2GroupFilterSet gfs = request.getFilterSet(); + /** + * returns a list of security groups + * + * @param request + * @return + */ + public EC2DescribeSecurityGroupsResponse describeSecurityGroups(EC2DescribeSecurityGroups request) + { + try { + EC2DescribeSecurityGroupsResponse response = listSecurityGroups( request.getGroupSet()); + EC2GroupFilterSet gfs = request.getFilterSet(); - if ( null == gfs ) - return response; - else return gfs.evaluate( response ); - } catch( Exception e ) { - logger.error( "EC2 DescribeSecurityGroups - ", e); - throw new EC2ServiceException(ServerError.InternalError, "An unexpected error occurred."); - } - } + if ( null == gfs ) + return response; + else return gfs.evaluate( response ); + } catch( Exception e ) { + logger.error( "EC2 DescribeSecurityGroups - ", e); + throw new EC2ServiceException(ServerError.InternalError, "An unexpected error occurred."); + } + } - /** - * CloudStack supports revoke only by using the ruleid of the ingress rule. - * We list all security groups and find the matching group and use the first ruleId we find. - * - * @param request - * @return - */ - public boolean revokeSecurityGroup( EC2AuthorizeRevokeSecurityGroup request ) - { - if (null == request.getName()) throw new EC2ServiceException(ServerError.InternalError, "Name is a required parameter"); - try { - String[] groupSet = new String[1]; - groupSet[0] = request.getName(); - String ruleId = null; - - EC2IpPermission[] items = request.getIpPermissionSet(); + /** + * CloudStack supports revoke only by using the ruleid of the ingress rule. + * We list all security groups and find the matching group and use the first ruleId we find. + * + * @param request + * @return + */ + public boolean revokeSecurityGroup( EC2AuthorizeRevokeSecurityGroup request ) + { + if (null == request.getName()) throw new EC2ServiceException(ServerError.InternalError, "Name is a required parameter"); + try { + String[] groupSet = new String[1]; + groupSet[0] = request.getName(); + String ruleId = null; - EC2DescribeSecurityGroupsResponse response = listSecurityGroups( groupSet ); - EC2SecurityGroup[] groups = response.getGroupSet(); + EC2IpPermission[] items = request.getIpPermissionSet(); - for (EC2SecurityGroup group : groups) { - EC2IpPermission[] perms = group.getIpPermissionSet(); - for (EC2IpPermission perm : perms) { - ruleId = doesRuleMatch( items[0], perm ); - if (ruleId != null) break; - } - } + EC2DescribeSecurityGroupsResponse response = listSecurityGroups( groupSet ); + EC2SecurityGroup[] groups = response.getGroupSet(); - if (null == ruleId) - throw new EC2ServiceException(ClientError.InvalidGroup_NotFound, "Cannot find matching ruleid."); + for (EC2SecurityGroup group : groups) { + EC2IpPermission[] perms = group.getIpPermissionSet(); + for (EC2IpPermission perm : perms) { + ruleId = doesRuleMatch( items[0], perm ); + if (ruleId != null) break; + } + } - CloudStackInfoResponse resp = getApi().revokeSecurityGroupIngress(ruleId); - if (resp != null && resp.getId() != null) { - return resp.getSuccess(); - } - return false; - } catch( Exception e ) { - logger.error( "EC2 revokeSecurityGroupIngress" + " - " + e.getMessage()); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - } + if (null == ruleId) + throw new EC2ServiceException(ClientError.InvalidGroup_NotFound, "Cannot find matching ruleid."); - /** - * authorizeSecurityGroup - * - * @param request - ip permission parameters - */ - public boolean authorizeSecurityGroup(EC2AuthorizeRevokeSecurityGroup request ) - { - if (null == request.getName()) throw new EC2ServiceException(ServerError.InternalError, "Name is a required parameter"); + CloudStackInfoResponse resp = getApi().revokeSecurityGroupIngress(ruleId); + if (resp != null && resp.getId() != null) { + return resp.getSuccess(); + } + return false; + } catch( Exception e ) { + logger.error( "EC2 revokeSecurityGroupIngress" + " - " + e.getMessage()); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } - EC2IpPermission[] items = request.getIpPermissionSet(); + /** + * authorizeSecurityGroup + * + * @param request - ip permission parameters + */ + public boolean authorizeSecurityGroup(EC2AuthorizeRevokeSecurityGroup request ) + { + if (null == request.getName()) throw new EC2ServiceException(ServerError.InternalError, "Name is a required parameter"); - try { - for (EC2IpPermission ipPerm : items) { - EC2SecurityGroup[] groups = ipPerm.getUserSet(); - - List secGroupList = new ArrayList(); - for (EC2SecurityGroup group : groups) { - CloudStackKeyValue pair = new CloudStackKeyValue(); - pair.setKeyValue(group.getAccount(), group.getName()); - secGroupList.add(pair); - } - CloudStackSecurityGroupIngress resp = null; - if (ipPerm.getProtocol().equalsIgnoreCase("icmp")) { - resp = getApi().authorizeSecurityGroupIngress(null, constructList(ipPerm.getIpRangeSet()), null, null, - ipPerm.getIcmpCode(), ipPerm.getIcmpType(), ipPerm.getProtocol(), null, - request.getName(), null, secGroupList); - } else { - resp = getApi().authorizeSecurityGroupIngress(null, constructList(ipPerm.getIpRangeSet()), null, - ipPerm.getToPort().longValue(), null, null, ipPerm.getProtocol(), null, request.getName(), - ipPerm.getFromPort().longValue(), secGroupList); - } - if (resp != null && resp.getRuleId() != null) { - return true; - } - return false; - } - } catch(Exception e) { - logger.error( "EC2 AuthorizeSecurityGroupIngress - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - return true; - } + EC2IpPermission[] items = request.getIpPermissionSet(); - /** - * Does the permission from the request (left) match the permission from the cloudStack query (right). - * If the cloudStack rule matches then we return its ruleId. - * - * @param permLeft - * @param permRight - * @return ruleId of the cloudstack rule - */ - private String doesRuleMatch(EC2IpPermission permLeft, EC2IpPermission permRight) - { - int matches = 0; + try { + for (EC2IpPermission ipPerm : items) { + EC2SecurityGroup[] groups = ipPerm.getUserSet(); - if (null != permLeft.getIcmpType() && null != permLeft.getIcmpCode()) { - if (null == permRight.getIcmpType() || null == permRight.getIcmpCode()) return null; + List secGroupList = new ArrayList(); + for (EC2SecurityGroup group : groups) { + CloudStackKeyValue pair = new CloudStackKeyValue(); + pair.setKeyValue(group.getAccount(), group.getName()); + secGroupList.add(pair); + } + CloudStackSecurityGroupIngress resp = null; + if (ipPerm.getProtocol().equalsIgnoreCase("icmp")) { + resp = getApi().authorizeSecurityGroupIngress(null, constructList(ipPerm.getIpRangeSet()), null, null, + ipPerm.getIcmpCode(), ipPerm.getIcmpType(), ipPerm.getProtocol(), null, + request.getName(), null, secGroupList); + } else { + resp = getApi().authorizeSecurityGroupIngress(null, constructList(ipPerm.getIpRangeSet()), null, + ipPerm.getToPort().longValue(), null, null, ipPerm.getProtocol(), null, request.getName(), + ipPerm.getFromPort().longValue(), secGroupList); + } + if (resp != null && resp.getRuleId() != null) { + return true; + } + return false; + } + } catch(Exception e) { + logger.error( "EC2 AuthorizeSecurityGroupIngress - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + return true; + } - if (!permLeft.getIcmpType().equalsIgnoreCase( permRight.getIcmpType())) return null; - if (!permLeft.getIcmpCode().equalsIgnoreCase( permRight.getIcmpCode())) return null; - matches++; - } + /** + * Does the permission from the request (left) match the permission from the cloudStack query (right). + * If the cloudStack rule matches then we return its ruleId. + * + * @param permLeft + * @param permRight + * @return ruleId of the cloudstack rule + */ + private String doesRuleMatch(EC2IpPermission permLeft, EC2IpPermission permRight) + { + int matches = 0; - // -> "Valid Values for EC2 security groups: tcp | udp | icmp or the corresponding protocol number (6 | 17 | 1)." - if (null != permLeft.getProtocol()) { - if (null == permRight.getProtocol()) return null; + if (null != permLeft.getIcmpType() && null != permLeft.getIcmpCode()) { + if (null == permRight.getIcmpType() || null == permRight.getIcmpCode()) return null; - String protocol = permLeft.getProtocol(); - if (protocol.equals( "6" )) protocol = "tcp"; - else if (protocol.equals( "17" )) protocol = "udp"; - else if (protocol.equals( "1" )) protocol = "icmp"; + if (!permLeft.getIcmpType().equalsIgnoreCase( permRight.getIcmpType())) return null; + if (!permLeft.getIcmpCode().equalsIgnoreCase( permRight.getIcmpCode())) return null; + matches++; + } - if (!protocol.equalsIgnoreCase( permRight.getProtocol())) return null; - matches++; - } + // -> "Valid Values for EC2 security groups: tcp | udp | icmp or the corresponding protocol number (6 | 17 | 1)." + if (null != permLeft.getProtocol()) { + if (null == permRight.getProtocol()) return null; + + String protocol = permLeft.getProtocol(); + if (protocol.equals( "6" )) protocol = "tcp"; + else if (protocol.equals( "17" )) protocol = "udp"; + else if (protocol.equals( "1" )) protocol = "icmp"; + + if (!protocol.equalsIgnoreCase( permRight.getProtocol())) return null; + matches++; + } - if (null != permLeft.getCIDR()) { - if (null == permRight.getCIDR()) return null; + if (null != permLeft.getCIDR()) { + if (null == permRight.getCIDR()) return null; - if (!permLeft.getCIDR().equalsIgnoreCase( permRight.getCIDR())) return null; - matches++; - } + if (!permLeft.getCIDR().equalsIgnoreCase( permRight.getCIDR())) return null; + matches++; + } - // -> is the port(s) from the request (left) a match of the rule's port(s) - if (0 != permLeft.getFromPort()) { - // -> -1 means all ports match - if (-1 != permLeft.getFromPort()) { - if (permLeft.getFromPort().compareTo(permRight.getFromPort()) != 0 || - permLeft.getToPort().compareTo(permRight.getToPort()) != 0) - return null; - } - matches++; - } + // -> is the port(s) from the request (left) a match of the rule's port(s) + if (0 != permLeft.getFromPort()) { + // -> -1 means all ports match + if (-1 != permLeft.getFromPort()) { + if (permLeft.getFromPort().compareTo(permRight.getFromPort()) != 0 || + permLeft.getToPort().compareTo(permRight.getToPort()) != 0) + return null; + } + matches++; + } - // -> was permLeft set up properly with at least one property to match? - if ( 0 == matches ) - return null; - else return permRight.getRuleId(); - } - - /** - * Returns a list of all snapshots - * - * @param request - * @return - */ + // -> was permLeft set up properly with at least one property to match? + if ( 0 == matches ) + return null; + else return permRight.getRuleId(); + } + + /** + * Returns a list of all snapshots + * + * @param request + * @return + */ public EC2DescribeSnapshotsResponse handleRequest( EC2DescribeSnapshots request ) - { - EC2DescribeVolumesResponse volumes = new EC2DescribeVolumesResponse(); - EC2SnapshotFilterSet sfs = request.getFilterSet(); + { + EC2DescribeVolumesResponse volumes = new EC2DescribeVolumesResponse(); + EC2SnapshotFilterSet sfs = request.getFilterSet(); EC2TagKeyValue[] tagKeyValueSet = request.getResourceTagSet(); - try { - // -> query to get the volume size for each snapshot + try { + // -> query to get the volume size for each snapshot EC2DescribeSnapshotsResponse response = listSnapshots( request.getSnapshotSet(), getResourceTags(tagKeyValueSet)); - if (response == null) { - return new EC2DescribeSnapshotsResponse(); - } - EC2Snapshot[] snapshots = response.getSnapshotSet(); - for (EC2Snapshot snap : snapshots) { - volumes = listVolumes(snap.getVolumeId(), null, volumes, null); - EC2Volume[] volSet = volumes.getVolumeSet(); - if (0 < volSet.length) snap.setVolumeSize(volSet[0].getSize()); - volumes.reset(); - } + if (response == null) { + return new EC2DescribeSnapshotsResponse(); + } + EC2Snapshot[] snapshots = response.getSnapshotSet(); + for (EC2Snapshot snap : snapshots) { + volumes = listVolumes(snap.getVolumeId(), null, volumes, null); + EC2Volume[] volSet = volumes.getVolumeSet(); + if (0 < volSet.length) snap.setVolumeSize(volSet[0].getSize()); + volumes.reset(); + } - if ( null == sfs ) - return response; - else return sfs.evaluate( response ); - } catch( EC2ServiceException error ) { - logger.error( "EC2 DescribeSnapshots - ", error); - throw error; + if ( null == sfs ) + return response; + else return sfs.evaluate( response ); + } catch( EC2ServiceException error ) { + logger.error( "EC2 DescribeSnapshots - ", error); + throw error; - } catch( Exception e ) { - logger.error( "EC2 DescribeSnapshots - ", e); - throw new EC2ServiceException(ServerError.InternalError, "An unexpected error occurred."); - } - } + } catch( Exception e ) { + logger.error( "EC2 DescribeSnapshots - ", e); + throw new EC2ServiceException(ServerError.InternalError, "An unexpected error occurred."); + } + } - /** - * Creates a snapshot - * - * @param volumeId - * @return - */ - public EC2Snapshot createSnapshot( String volumeId ) { - try { - - CloudStackSnapshot snap = getApi().createSnapshot(volumeId, null, null, null); - if (snap == null) { - throw new EC2ServiceException(ServerError.InternalError, "Unable to create snapshot!"); - } - EC2Snapshot ec2Snapshot = new EC2Snapshot(); + /** + * Creates a snapshot + * + * @param volumeId + * @return + */ + public EC2Snapshot createSnapshot( String volumeId ) { + try { - ec2Snapshot.setId(snap.getId()); - ec2Snapshot.setName(snap.getName()); - ec2Snapshot.setType(snap.getSnapshotType()); - ec2Snapshot.setAccountName(snap.getAccountName()); - ec2Snapshot.setDomainId(snap.getDomainId()); - ec2Snapshot.setCreated(snap.getCreated()); - ec2Snapshot.setVolumeId(snap.getVolumeId()); - - List vols = getApi().listVolumes(null, null, null, snap.getVolumeId(), null, null, null, null, null, null, null, null); + CloudStackSnapshot snap = getApi().createSnapshot(volumeId, null, null, null); + if (snap == null) { + throw new EC2ServiceException(ServerError.InternalError, "Unable to create snapshot!"); + } + EC2Snapshot ec2Snapshot = new EC2Snapshot(); - if(vols.size() > 0) { - assert(vols.get(0).getSize() != null); - Long sizeInGB = vols.get(0).getSize().longValue()/1073741824; - ec2Snapshot.setVolumeSize(sizeInGB); - } + ec2Snapshot.setId(snap.getId()); + ec2Snapshot.setName(snap.getName()); + ec2Snapshot.setType(snap.getSnapshotType()); + ec2Snapshot.setAccountName(snap.getAccountName()); + ec2Snapshot.setDomainId(snap.getDomainId()); + ec2Snapshot.setCreated(snap.getCreated()); + ec2Snapshot.setVolumeId(snap.getVolumeId()); - return ec2Snapshot; - } catch( Exception e ) { - logger.error( "EC2 CreateSnapshot - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - } + List vols = getApi().listVolumes(null, null, null, snap.getVolumeId(), null, null, null, null, null, null, null, null); - /** - * Deletes a snapshot - * - * @param snapshotId - * @return - */ - public boolean deleteSnapshot(String snapshotId) { - try { - - CloudStackInfoResponse resp = getApi().deleteSnapshot(snapshotId); - if(resp != null) { - return resp.getSuccess(); - } + if(vols.size() > 0) { + assert(vols.get(0).getSize() != null); + Long sizeInGB = vols.get(0).getSize().longValue()/1073741824; + ec2Snapshot.setVolumeSize(sizeInGB); + } - return false; - } catch(Exception e) { - logger.error( "EC2 DeleteSnapshot - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } - - - /** REST API calls this method. + return ec2Snapshot; + } catch( Exception e ) { + logger.error( "EC2 CreateSnapshot - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } + + /** + * Deletes a snapshot + * + * @param snapshotId + * @return + */ + public boolean deleteSnapshot(String snapshotId) { + try { + + CloudStackInfoResponse resp = getApi().deleteSnapshot(snapshotId); + if(resp != null) { + return resp.getSuccess(); + } + + return false; + } catch(Exception e) { + logger.error( "EC2 DeleteSnapshot - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } + + + /** REST API calls this method. * Modify an existing template * * @param request @@ -536,7 +527,7 @@ public class EC2Engine { try { images = listTemplates( request.getId(), images ); EC2Image[] imageSet = images.getImageSet(); - + CloudStackTemplate resp = getApi().updateTemplate(request.getId(), null, request.getDescription(), null, imageSet[0].getName(), null, null); if (resp != null) { return true; @@ -549,21 +540,21 @@ public class EC2Engine { } - /** - * Modify an existing template - * - * @param request - * @return - */ - public boolean modifyImageAttribute( EC2ModifyImageAttribute request ) - { + /** + * Modify an existing template + * + * @param request + * @return + */ + public boolean modifyImageAttribute( EC2ModifyImageAttribute request ) + { try { if(request.getAttribute().equals(ImageAttribute.launchPermission)){ - + String accounts = ""; Boolean isPublic = null; EC2ModifyImageAttribute.Operation operation = request.getLaunchPermOperation(); - + List accountOrGroupList = request.getLaunchPermissionAccountsList(); if(accountOrGroupList != null && !accountOrGroupList.isEmpty()){ boolean first = true; @@ -597,25 +588,25 @@ public class EC2Engine { logger.error( "EC2 modifyImageAttribute - ", e); throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); } - + return false; - - - } - + + + } + public EC2ImageAttributes describeImageAttribute(EC2DescribeImageAttribute request) { EC2ImageAttributes imageAtts = new EC2ImageAttributes(); - + try { imageAtts.setImageId(request.getImageId()); if(request.getAttribute().equals(ImageAttribute.launchPermission)){ CloudStackTemplatePermission tempPerm = getApi().listTemplatePermissions(request.getImageId(), null, null); if(tempPerm != null){ imageAtts.setDomainId(tempPerm.getDomainId()); - + List accntList = tempPerm.getAccounts(); imageAtts.setAccountNamesWithLaunchPermission(accntList); - + imageAtts.setIsPublic(tempPerm.getIsPublic()); } }else if(request.getAttribute().equals(ImageAttribute.description)){ @@ -631,47 +622,47 @@ public class EC2Engine { logger.error( "EC2 describeImageAttribute - ", e); throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); } - + return imageAtts; } - - /** - * If given a specific list of snapshots of interest, then only values from those snapshots are returned. - * - * @param interestedShots - can be null, should be a subset of all snapshots - */ + + /** + * If given a specific list of snapshots of interest, then only values from those snapshots are returned. + * + * @param interestedShots - can be null, should be a subset of all snapshots + */ private EC2DescribeSnapshotsResponse listSnapshots( String[] interestedShots, List resourceTagSet ) throws Exception { - EC2DescribeSnapshotsResponse snapshots = new EC2DescribeSnapshotsResponse(); + EC2DescribeSnapshotsResponse snapshots = new EC2DescribeSnapshotsResponse(); - List cloudSnaps; - if (interestedShots == null || interestedShots.length == 0) { + List cloudSnaps; + if (interestedShots == null || interestedShots.length == 0) { cloudSnaps = getApi().listSnapshots(null, null, null, null, null, null, null, null, null, resourceTagSet); - } else { - cloudSnaps = new ArrayList(); + } else { + cloudSnaps = new ArrayList(); - for(String id : interestedShots) { + for(String id : interestedShots) { List tmpList = getApi().listSnapshots(null, null, id, null, null, null, null, - null, null, resourceTagSet); - cloudSnaps.addAll(tmpList); - } - } + null, null, resourceTagSet); + cloudSnaps.addAll(tmpList); + } + } - if (cloudSnaps == null) { - return null; - } + if (cloudSnaps == null) { + return null; + } - for(CloudStackSnapshot cloudSnapshot : cloudSnaps) { - EC2Snapshot shot = new EC2Snapshot(); - shot.setId(cloudSnapshot.getId()); - shot.setName(cloudSnapshot.getName()); - shot.setVolumeId(cloudSnapshot.getVolumeId()); - shot.setType(cloudSnapshot.getSnapshotType()); - shot.setState(cloudSnapshot.getState()); - shot.setCreated(cloudSnapshot.getCreated()); - shot.setAccountName(cloudSnapshot.getAccountName()); - shot.setDomainId(cloudSnapshot.getDomainId()); + for(CloudStackSnapshot cloudSnapshot : cloudSnaps) { + EC2Snapshot shot = new EC2Snapshot(); + shot.setId(cloudSnapshot.getId()); + shot.setName(cloudSnapshot.getName()); + shot.setVolumeId(cloudSnapshot.getVolumeId()); + shot.setType(cloudSnapshot.getSnapshotType()); + shot.setState(cloudSnapshot.getState()); + shot.setCreated(cloudSnapshot.getCreated()); + shot.setAccountName(cloudSnapshot.getAccountName()); + shot.setDomainId(cloudSnapshot.getDomainId()); List resourceTags = cloudSnapshot.getTags(); for(CloudStackKeyValue resourceTag : resourceTags) { @@ -682,470 +673,470 @@ public class EC2Engine { shot.addResourceTag(param); } - snapshots.addSnapshot(shot); - } - return snapshots; - } + snapshots.addSnapshot(shot); + } + return snapshots; + } - // handlers - /** - * return password data from the instance - * - * @param instanceId - * @return - */ - public EC2PasswordData getPasswordData(String instanceId) { - try { - CloudStackPasswordData resp = getApi().getVMPassword(instanceId); - EC2PasswordData passwdData = new EC2PasswordData(); - if (resp != null) { - passwdData.setInstanceId(instanceId); - passwdData.setEncryptedPassword(resp.getEncryptedpassword()); - } - return passwdData; - } catch(Exception e) { - logger.error("EC2 GetPasswordData - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - } - /** - * Lists SSH KeyPairs on the systme - * - * @param request - * @return - */ - public EC2DescribeKeyPairsResponse describeKeyPairs( EC2DescribeKeyPairs request ) { - try { - EC2KeyPairFilterSet filterSet = request.getKeyFilterSet(); - String[] keyNames = request.getKeyNames(); - List keyPairs = getApi().listSSHKeyPairs(null, null, null); - List keyPairsList = new ArrayList(); - - if (keyPairs != null) { - // Let's trim the list of keypairs to only the ones listed in keyNames - List matchedKeyPairs = new ArrayList(); - if (keyNames != null && keyNames.length > 0) { - for (CloudStackKeyPair keyPair : keyPairs) { - boolean matched = false; - for (String keyName : keyNames) { - if (keyPair.getName().equalsIgnoreCase(keyName)) { - matched = true; - break; - } - } - if (matched) { - matchedKeyPairs.add(keyPair); - } - } - if (matchedKeyPairs.isEmpty()) { - throw new EC2ServiceException(ServerError.InternalError, "No matching keypairs found"); - } - }else{ - matchedKeyPairs = keyPairs; - } - - - // this should be reworked... converting from CloudStackKeyPairResponse to EC2SSHKeyPair is dumb - for (CloudStackKeyPair respKeyPair: matchedKeyPairs) { - EC2SSHKeyPair ec2KeyPair = new EC2SSHKeyPair(); - ec2KeyPair.setFingerprint(respKeyPair.getFingerprint()); - ec2KeyPair.setKeyName(respKeyPair.getName()); - ec2KeyPair.setPrivateKey(respKeyPair.getPrivatekey()); - keyPairsList.add(ec2KeyPair); - } - } - return filterSet.evaluate(keyPairsList); - } catch(Exception e) { - logger.error("EC2 DescribeKeyPairs - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - } + // handlers + /** + * return password data from the instance + * + * @param instanceId + * @return + */ + public EC2PasswordData getPasswordData(String instanceId) { + try { + CloudStackPasswordData resp = getApi().getVMPassword(instanceId); + EC2PasswordData passwdData = new EC2PasswordData(); + if (resp != null) { + passwdData.setInstanceId(instanceId); + passwdData.setEncryptedPassword(resp.getEncryptedpassword()); + } + return passwdData; + } catch(Exception e) { + logger.error("EC2 GetPasswordData - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } + /** + * Lists SSH KeyPairs on the systme + * + * @param request + * @return + */ + public EC2DescribeKeyPairsResponse describeKeyPairs( EC2DescribeKeyPairs request ) { + try { + EC2KeyPairFilterSet filterSet = request.getKeyFilterSet(); + String[] keyNames = request.getKeyNames(); + List keyPairs = getApi().listSSHKeyPairs(null, null, null); + List keyPairsList = new ArrayList(); - /** - * Delete SSHKeyPair - * - * @param request - * @return - */ - public boolean deleteKeyPair( EC2DeleteKeyPair request ) { - try { - CloudStackInfoResponse resp = getApi().deleteSSHKeyPair(request.getKeyName(), null, null); - if (resp == null) { - throw new Exception("Ivalid CloudStack API response"); - } + if (keyPairs != null) { + // Let's trim the list of keypairs to only the ones listed in keyNames + List matchedKeyPairs = new ArrayList(); + if (keyNames != null && keyNames.length > 0) { + for (CloudStackKeyPair keyPair : keyPairs) { + boolean matched = false; + for (String keyName : keyNames) { + if (keyPair.getName().equalsIgnoreCase(keyName)) { + matched = true; + break; + } + } + if (matched) { + matchedKeyPairs.add(keyPair); + } + } + if (matchedKeyPairs.isEmpty()) { + throw new EC2ServiceException(ServerError.InternalError, "No matching keypairs found"); + } + }else{ + matchedKeyPairs = keyPairs; + } - return resp.getSuccess(); - } catch(Exception e) { - logger.error("EC2 DeleteKeyPair - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - } - /** - * Create SSHKeyPair - * - * @param request - * @return - */ - public EC2SSHKeyPair createKeyPair(EC2CreateKeyPair request) { - try { - CloudStackKeyPair resp = getApi().createSSHKeyPair(request.getKeyName(), null, null); - if (resp == null) { - throw new Exception("Ivalid CloudStack API response"); - } + // this should be reworked... converting from CloudStackKeyPairResponse to EC2SSHKeyPair is dumb + for (CloudStackKeyPair respKeyPair: matchedKeyPairs) { + EC2SSHKeyPair ec2KeyPair = new EC2SSHKeyPair(); + ec2KeyPair.setFingerprint(respKeyPair.getFingerprint()); + ec2KeyPair.setKeyName(respKeyPair.getName()); + ec2KeyPair.setPrivateKey(respKeyPair.getPrivatekey()); + keyPairsList.add(ec2KeyPair); + } + } + return filterSet.evaluate(keyPairsList); + } catch(Exception e) { + logger.error("EC2 DescribeKeyPairs - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } - EC2SSHKeyPair response = new EC2SSHKeyPair(); - response.setFingerprint(resp.getFingerprint()); - response.setKeyName(resp.getName()); - response.setPrivateKey(resp.getPrivatekey()); + /** + * Delete SSHKeyPair + * + * @param request + * @return + */ + public boolean deleteKeyPair( EC2DeleteKeyPair request ) { + try { + CloudStackInfoResponse resp = getApi().deleteSSHKeyPair(request.getKeyName(), null, null); + if (resp == null) { + throw new Exception("Ivalid CloudStack API response"); + } - return response; - } catch (Exception e) { - logger.error("EC2 CreateKeyPair - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - } + return resp.getSuccess(); + } catch(Exception e) { + logger.error("EC2 DeleteKeyPair - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } - /** - * Import an existing SSH KeyPair - * - * @param request - * @return - */ - public EC2SSHKeyPair importKeyPair( EC2ImportKeyPair request ) { - try { - CloudStackKeyPair resp = getApi().registerSSHKeyPair(request.getKeyName(), request.getPublicKeyMaterial()); - if (resp == null) { - throw new Exception("Ivalid CloudStack API response"); - } + /** + * Create SSHKeyPair + * + * @param request + * @return + */ + public EC2SSHKeyPair createKeyPair(EC2CreateKeyPair request) { + try { + CloudStackKeyPair resp = getApi().createSSHKeyPair(request.getKeyName(), null, null); + if (resp == null) { + throw new Exception("Ivalid CloudStack API response"); + } - EC2SSHKeyPair response = new EC2SSHKeyPair(); - response.setFingerprint(resp.getFingerprint()); - response.setKeyName(resp.getName()); - response.setPrivateKey(resp.getPrivatekey()); + EC2SSHKeyPair response = new EC2SSHKeyPair(); + response.setFingerprint(resp.getFingerprint()); + response.setKeyName(resp.getName()); + response.setPrivateKey(resp.getPrivatekey()); - return response; - } catch (Exception e) { - logger.error("EC2 ImportKeyPair - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - } + return response; + } catch (Exception e) { + logger.error("EC2 CreateKeyPair - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } - /** - * list ip addresses that have been allocated - * - * @param request - * @return - */ - public EC2DescribeAddressesResponse describeAddresses( EC2DescribeAddresses request ) { - try { - List addrList = getApi().listPublicIpAddresses(null, null, null, null, null, null, null, null, null); + /** + * Import an existing SSH KeyPair + * + * @param request + * @return + */ + public EC2SSHKeyPair importKeyPair( EC2ImportKeyPair request ) { + try { + CloudStackKeyPair resp = getApi().registerSSHKeyPair(request.getKeyName(), request.getPublicKeyMaterial()); + if (resp == null) { + throw new Exception("Ivalid CloudStack API response"); + } - EC2AddressFilterSet filterSet = request.getFilterSet(); - List addressList = new ArrayList(); - if (addrList != null && addrList.size() > 0) { - for (CloudStackIpAddress addr: addrList) { - // remember, if no filters are set, request.inPublicIpSet always returns true - if (request.inPublicIpSet(addr.getIpAddress())) { - EC2Address ec2Address = new EC2Address(); - ec2Address.setIpAddress(addr.getIpAddress()); - if (addr.getVirtualMachineId() != null) - ec2Address.setAssociatedInstanceId(addr.getVirtualMachineId().toString()); - addressList.add(ec2Address); - } - } - } + EC2SSHKeyPair response = new EC2SSHKeyPair(); + response.setFingerprint(resp.getFingerprint()); + response.setKeyName(resp.getName()); + response.setPrivateKey(resp.getPrivatekey()); - return filterSet.evaluate(addressList); - } catch(Exception e) { - logger.error("EC2 DescribeAddresses - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - } + return response; + } catch (Exception e) { + logger.error("EC2 ImportKeyPair - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } - /** - * release an IP Address - * - * @param request - * @return - */ - public boolean releaseAddress(EC2ReleaseAddress request) { - try { - CloudStackIpAddress cloudIp = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null).get(0); - CloudStackInfoResponse resp = getApi().disassociateIpAddress(cloudIp.getId()); - if (resp != null) { - return resp.getSuccess(); - } - } catch(Exception e) { - logger.error("EC2 ReleaseAddress - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - return false; - } + /** + * list ip addresses that have been allocated + * + * @param request + * @return + */ + public EC2DescribeAddressesResponse describeAddresses( EC2DescribeAddresses request ) { + try { + List addrList = getApi().listPublicIpAddresses(null, null, null, null, null, null, null, null, null); - /** - * Associate an address with an instance - * - * @param request - * @return - */ - public boolean associateAddress( EC2AssociateAddress request ) { - try { - CloudStackIpAddress cloudIp = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null).get(0); - CloudStackUserVm cloudVm = getApi().listVirtualMachines(null, null, true, null, null, null, null, request.getInstanceId(), null, null, null, null, null, null, null, null, null).get(0); + EC2AddressFilterSet filterSet = request.getFilterSet(); + List addressList = new ArrayList(); + if (addrList != null && addrList.size() > 0) { + for (CloudStackIpAddress addr: addrList) { + // remember, if no filters are set, request.inPublicIpSet always returns true + if (request.inPublicIpSet(addr.getIpAddress())) { + EC2Address ec2Address = new EC2Address(); + ec2Address.setIpAddress(addr.getIpAddress()); + if (addr.getVirtualMachineId() != null) + ec2Address.setAssociatedInstanceId(addr.getVirtualMachineId().toString()); + addressList.add(ec2Address); + } + } + } - CloudStackInfoResponse resp = getApi().enableStaticNat(cloudIp.getId(), cloudVm.getId()); - if (resp != null) { - return resp.getSuccess(); - } - } catch(Exception e) { - logger.error( "EC2 AssociateAddress - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - return false; - } + return filterSet.evaluate(addressList); + } catch(Exception e) { + logger.error("EC2 DescribeAddresses - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } - /** - * Disassociate an address from an instance - * - * @param request - * @return - */ - public boolean disassociateAddress( EC2DisassociateAddress request ) { - try { - CloudStackIpAddress cloudIp = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null).get(0); - CloudStackInfoResponse resp = getApi().disableStaticNat(cloudIp.getId()); - if (resp != null) { - return resp.getSuccess(); - } - } catch(Exception e) { - logger.error( "EC2 DisassociateAddress - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - return false; - } + /** + * release an IP Address + * + * @param request + * @return + */ + public boolean releaseAddress(EC2ReleaseAddress request) { + try { + CloudStackIpAddress cloudIp = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null).get(0); + CloudStackInfoResponse resp = getApi().disassociateIpAddress(cloudIp.getId()); + if (resp != null) { + return resp.getSuccess(); + } + } catch(Exception e) { + logger.error("EC2 ReleaseAddress - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + return false; + } - /** - * Allocate an address - * - * @param request - * @return - */ - public EC2Address allocateAddress() - { - try { + /** + * Associate an address with an instance + * + * @param request + * @return + */ + public boolean associateAddress( EC2AssociateAddress request ) { + try { + CloudStackIpAddress cloudIp = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null).get(0); + CloudStackUserVm cloudVm = getApi().listVirtualMachines(null, null, true, null, null, null, null, request.getInstanceId(), null, null, null, null, null, null, null, null, null).get(0); + + CloudStackInfoResponse resp = getApi().enableStaticNat(cloudIp.getId(), cloudVm.getId()); + if (resp != null) { + return resp.getSuccess(); + } + } catch(Exception e) { + logger.error( "EC2 AssociateAddress - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + return false; + } + + /** + * Disassociate an address from an instance + * + * @param request + * @return + */ + public boolean disassociateAddress( EC2DisassociateAddress request ) { + try { + CloudStackIpAddress cloudIp = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null).get(0); + CloudStackInfoResponse resp = getApi().disableStaticNat(cloudIp.getId()); + if (resp != null) { + return resp.getSuccess(); + } + } catch(Exception e) { + logger.error( "EC2 DisassociateAddress - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + return false; + } + + /** + * Allocate an address + * + * @param request + * @return + */ + public EC2Address allocateAddress() + { + try { EC2Address ec2Address = new EC2Address(); // this gets our networkId CloudStackAccount caller = getCurrentAccount(); - + CloudStackZone zone = findZone(); CloudStackNetwork net = findNetwork(zone); // CloudStackIpAddress resp = getApi().associateIpAddress(null, null, null, "0036952d-48df-4422-9fd0-94b0885e18cb"); CloudStackIpAddress resp = getApi().associateIpAddress(zone.getId(), caller.getName(), caller.getDomainId(), net != null ? net.getId():null); - ec2Address.setAssociatedInstanceId(resp.getId()); - - if (resp.getIpAddress() == null) { - List addrList = getApi().listPublicIpAddresses(null, null, null, null, null, null, null, null, null); - if (addrList != null && addrList.size() > 0) { - for (CloudStackIpAddress addr: addrList) { - if (addr.getId().equalsIgnoreCase(resp.getId())) { - ec2Address.setIpAddress(addr.getIpAddress()); - } - } - } - } else { - ec2Address.setIpAddress(resp.getIpAddress()); - } + ec2Address.setAssociatedInstanceId(resp.getId()); - return ec2Address; - } catch(Exception e) { - logger.error( "EC2 AllocateAddress - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + if (resp.getIpAddress() == null) { + List addrList = getApi().listPublicIpAddresses(null, null, null, null, null, null, null, null, null); + if (addrList != null && addrList.size() > 0) { + for (CloudStackIpAddress addr: addrList) { + if (addr.getId().equalsIgnoreCase(resp.getId())) { + ec2Address.setIpAddress(addr.getIpAddress()); + } + } + } + } else { + ec2Address.setIpAddress(resp.getIpAddress()); + } - /** - * List of templates available. We only support the imageSet version of this call or when no search parameters are passed - * which results in asking for all templates. - * - * @param request - * @return - */ - public EC2DescribeImagesResponse describeImages(EC2DescribeImages request) - { - EC2DescribeImagesResponse images = new EC2DescribeImagesResponse(); + return ec2Address; + } catch(Exception e) { + logger.error( "EC2 AllocateAddress - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } - try { - String[] templateIds = request.getImageSet(); + /** + * List of templates available. We only support the imageSet version of this call or when no search parameters are passed + * which results in asking for all templates. + * + * @param request + * @return + */ + public EC2DescribeImagesResponse describeImages(EC2DescribeImages request) + { + EC2DescribeImagesResponse images = new EC2DescribeImagesResponse(); - if ( 0 == templateIds.length ) { - return listTemplates(null, images); - } - for (String s : templateIds) { - images = listTemplates(s, images); - } - return images; + try { + String[] templateIds = request.getImageSet(); - } catch( Exception e ) { - logger.error( "EC2 DescribeImages - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + if ( 0 == templateIds.length ) { + return listTemplates(null, images); + } + for (String s : templateIds) { + images = listTemplates(s, images); + } + return images; - /** - * Create a template - * Amazon API just gives us the instanceId to create the template from. - * But our createTemplate function requires the volumeId and osTypeId. - * So to get that we must make the following sequence of cloud API calls: - * 1) listVolumes&virtualMachineId= -- gets the volumeId - * 2) listVirtualMachinees&id= -- gets the templateId - * 3) listTemplates&id= -- gets the osTypeId - * - * If we have to start and stop the VM in question then this function is - * going to take a long time to complete. - * - * @param request - * @return - */ - public EC2CreateImageResponse createImage(EC2CreateImage request) - { - EC2CreateImageResponse response = null; - boolean needsRestart = false; - String volumeId = null; + } catch( Exception e ) { + logger.error( "EC2 DescribeImages - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } - try { - // [A] Creating a template from a VM volume should be from the ROOT volume - // Also for this to work the VM must be in a Stopped state so we 'reboot' it if its not - EC2DescribeVolumesResponse volumes = new EC2DescribeVolumesResponse(); - volumes = listVolumes( null, request.getInstanceId(), volumes, null ); - EC2Volume[] volSet = volumes.getVolumeSet(); - for (EC2Volume vol : volSet) { - if (vol.getType().equalsIgnoreCase( "ROOT" )) { - String vmState = vol.getVMState(); - if (vmState.equalsIgnoreCase( "running" ) || vmState.equalsIgnoreCase( "starting" )) { - needsRestart = true; - if (!stopVirtualMachine( request.getInstanceId() )) - throw new EC2ServiceException(ClientError.IncorrectState, "CreateImage - instance must be in a stopped state"); - } - volumeId = vol.getId(); - break; - } - } + /** + * Create a template + * Amazon API just gives us the instanceId to create the template from. + * But our createTemplate function requires the volumeId and osTypeId. + * So to get that we must make the following sequence of cloud API calls: + * 1) listVolumes&virtualMachineId= -- gets the volumeId + * 2) listVirtualMachinees&id= -- gets the templateId + * 3) listTemplates&id= -- gets the osTypeId + * + * If we have to start and stop the VM in question then this function is + * going to take a long time to complete. + * + * @param request + * @return + */ + public EC2CreateImageResponse createImage(EC2CreateImage request) + { + EC2CreateImageResponse response = null; + boolean needsRestart = false; + String volumeId = null; - // [B] The parameters must be in sorted order for proper signature generation - EC2DescribeInstancesResponse instances = new EC2DescribeInstancesResponse(); - instances = lookupInstances( request.getInstanceId(), instances, null ); - EC2Instance[] instanceSet = instances.getInstanceSet(); - String templateId = instanceSet[0].getTemplateId(); + try { + // [A] Creating a template from a VM volume should be from the ROOT volume + // Also for this to work the VM must be in a Stopped state so we 'reboot' it if its not + EC2DescribeVolumesResponse volumes = new EC2DescribeVolumesResponse(); + volumes = listVolumes( null, request.getInstanceId(), volumes, null ); + EC2Volume[] volSet = volumes.getVolumeSet(); + for (EC2Volume vol : volSet) { + if (vol.getType().equalsIgnoreCase( "ROOT" )) { + String vmState = vol.getVMState(); + if (vmState.equalsIgnoreCase( "running" ) || vmState.equalsIgnoreCase( "starting" )) { + needsRestart = true; + if (!stopVirtualMachine( request.getInstanceId() )) + throw new EC2ServiceException(ClientError.IncorrectState, "CreateImage - instance must be in a stopped state"); + } + volumeId = vol.getId(); + break; + } + } - EC2DescribeImagesResponse images = new EC2DescribeImagesResponse(); - images = listTemplates( templateId, images ); - EC2Image[] imageSet = images.getImageSet(); - String osTypeId = imageSet[0].getOsTypeId(); - - CloudStackTemplate resp = getApi().createTemplate((request.getDescription() == null ? "" : request.getDescription()), request.getName(), - osTypeId, null, null, null, null, null, null, volumeId); - if (resp == null || resp.getId() == null) { - throw new EC2ServiceException(ServerError.InternalError, "An upexpected error occurred."); - } - - //if template was created succesfully, create the new image response - response = new EC2CreateImageResponse(); - response.setId(resp.getId()); + // [B] The parameters must be in sorted order for proper signature generation + EC2DescribeInstancesResponse instances = new EC2DescribeInstancesResponse(); + instances = lookupInstances( request.getInstanceId(), instances, null ); + EC2Instance[] instanceSet = instances.getInstanceSet(); + String templateId = instanceSet[0].getTemplateId(); - // [C] If we stopped the virtual machine now we need to restart it - if (needsRestart) { - if (!startVirtualMachine( request.getInstanceId() )) - throw new EC2ServiceException(ServerError.InternalError, - "CreateImage - restarting instance " + request.getInstanceId() + " failed"); - } - return response; + EC2DescribeImagesResponse images = new EC2DescribeImagesResponse(); + images = listTemplates( templateId, images ); + EC2Image[] imageSet = images.getImageSet(); + String osTypeId = imageSet[0].getOsTypeId(); - } catch( Exception e ) { - logger.error( "EC2 CreateImage - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + CloudStackTemplate resp = getApi().createTemplate((request.getDescription() == null ? "" : request.getDescription()), request.getName(), + osTypeId, null, null, null, null, null, null, volumeId); + if (resp == null || resp.getId() == null) { + throw new EC2ServiceException(ServerError.InternalError, "An upexpected error occurred."); + } - /** - * Register a template - * - * @param request - * @return - */ - public EC2CreateImageResponse registerImage(EC2RegisterImage request) - { - try { - CloudStackAccount caller = getCurrentAccount(); + //if template was created succesfully, create the new image response + response = new EC2CreateImageResponse(); + response.setId(resp.getId()); + + // [C] If we stopped the virtual machine now we need to restart it + if (needsRestart) { + if (!startVirtualMachine( request.getInstanceId() )) + throw new EC2ServiceException(ServerError.InternalError, + "CreateImage - restarting instance " + request.getInstanceId() + " failed"); + } + return response; + + } catch( Exception e ) { + logger.error( "EC2 CreateImage - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } + + /** + * Register a template + * + * @param request + * @return + */ + public EC2CreateImageResponse registerImage(EC2RegisterImage request) + { + try { + CloudStackAccount caller = getCurrentAccount(); if (null == request.getName()) throw new EC2ServiceException(ClientError.Unsupported, "Missing parameter - name"); - List templates = getApi().registerTemplate((request.getDescription() == null ? request.getName() : request.getDescription()), - request.getFormat(), request.getHypervisor(), request.getName(), toOSTypeId(request.getOsTypeName()), request.getLocation(), - toZoneId(request.getZoneName(), null), null, null, null, null, null, null, null, null, null); - if (templates != null) { - // technically we will only ever register a single template... - for (CloudStackTemplate template : templates) { - if (template != null && template.getId() != null) { - EC2CreateImageResponse image = new EC2CreateImageResponse(); - image.setId(template.getId().toString()); - return image; - } - } - } - return null; - } catch( Exception e ) { - logger.error( "EC2 RegisterImage - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + List templates = getApi().registerTemplate((request.getDescription() == null ? request.getName() : request.getDescription()), + request.getFormat(), request.getHypervisor(), request.getName(), toOSTypeId(request.getOsTypeName()), request.getLocation(), + toZoneId(request.getZoneName(), null), null, null, null, null, null, null, null, null, null); + if (templates != null) { + // technically we will only ever register a single template... + for (CloudStackTemplate template : templates) { + if (template != null && template.getId() != null) { + EC2CreateImageResponse image = new EC2CreateImageResponse(); + image.setId(template.getId().toString()); + return image; + } + } + } + return null; + } catch( Exception e ) { + logger.error( "EC2 RegisterImage - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } - /** - * Deregister a template(image) - * Our implementation is different from Amazon in that we do delete the template - * when we deregister it. The cloud API has not deregister call. - * - * @param image - * @return - */ - public boolean deregisterImage( EC2Image image ) - { - try { - CloudStackInfoResponse resp = getApi().deleteTemplate(image.getId(), null); - return resp.getSuccess(); - } catch( Exception e ) { - logger.error( "EC2 DeregisterImage - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + /** + * Deregister a template(image) + * Our implementation is different from Amazon in that we do delete the template + * when we deregister it. The cloud API has not deregister call. + * + * @param image + * @return + */ + public boolean deregisterImage( EC2Image image ) + { + try { + CloudStackInfoResponse resp = getApi().deleteTemplate(image.getId(), null); + return resp.getSuccess(); + } catch( Exception e ) { + logger.error( "EC2 DeregisterImage - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } - /** - * list instances - * - * @param request - * @return - */ - public EC2DescribeInstancesResponse describeInstances(EC2DescribeInstances request ) { - try { + /** + * list instances + * + * @param request + * @return + */ + public EC2DescribeInstancesResponse describeInstances(EC2DescribeInstances request ) { + try { EC2TagKeyValue[] tagKeyValueSet = request.getResourceTagSet(); return listVirtualMachines( request.getInstancesSet(), request.getFilterSet(), getResourceTags(tagKeyValueSet)); - } catch( Exception e ) { - logger.error( "EC2 DescribeInstances - " ,e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + } catch( Exception e ) { + logger.error( "EC2 DescribeInstances - " ,e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } - /** - * list Zones - * - * @param request - * @return - */ - public EC2DescribeAvailabilityZonesResponse handleRequest(EC2DescribeAvailabilityZones request) { - try { - EC2DescribeAvailabilityZonesResponse availableZones = listZones(request.getZoneSet(), null); + /** + * list Zones + * + * @param request + * @return + */ + public EC2DescribeAvailabilityZonesResponse handleRequest(EC2DescribeAvailabilityZones request) { + try { + EC2DescribeAvailabilityZonesResponse availableZones = listZones(request.getZoneSet(), null); EC2AvailabilityZonesFilterSet azfs = request.getFilterSet(); if ( null == azfs ) return availableZones; @@ -1155,185 +1146,185 @@ public class EC2Engine { return new EC2DescribeAvailabilityZonesResponse(); return listZones(matchedAvailableZones.toArray(new String[0]), null); } - } catch( EC2ServiceException error ) { - logger.error( "EC2 DescribeAvailabilityZones - ", error); - throw error; + } catch( EC2ServiceException error ) { + logger.error( "EC2 DescribeAvailabilityZones - ", error); + throw error; - } catch( Exception e ) { - logger.error( "EC2 DescribeAvailabilityZones - " ,e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + } catch( Exception e ) { + logger.error( "EC2 DescribeAvailabilityZones - " ,e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } - /** - * list volumes - * - * @param request - * @return - */ - public EC2DescribeVolumesResponse handleRequest( EC2DescribeVolumes request ) { - EC2DescribeVolumesResponse volumes = new EC2DescribeVolumesResponse(); - EC2VolumeFilterSet vfs = request.getFilterSet(); + /** + * list volumes + * + * @param request + * @return + */ + public EC2DescribeVolumesResponse handleRequest( EC2DescribeVolumes request ) { + EC2DescribeVolumesResponse volumes = new EC2DescribeVolumesResponse(); + EC2VolumeFilterSet vfs = request.getFilterSet(); EC2TagKeyValue[] tagKeyValueSet = request.getResourceTagSet(); - try { - String[] volumeIds = request.getVolumeSet(); - if ( 0 == volumeIds.length ){ + try { + String[] volumeIds = request.getVolumeSet(); + if ( 0 == volumeIds.length ){ volumes = listVolumes( null, null, volumes, getResourceTags(tagKeyValueSet) ); - } else { - for (String s : volumeIds) + } else { + for (String s : volumeIds) volumes = listVolumes(s, null, volumes, getResourceTags(tagKeyValueSet) ); - } + } - if ( null == vfs ) - return volumes; - else return vfs.evaluate( volumes ); - } catch( Exception e ) { - logger.error( "EC2 DescribeVolumes - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + if ( null == vfs ) + return volumes; + else return vfs.evaluate( volumes ); + } catch( Exception e ) { + logger.error( "EC2 DescribeVolumes - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } - /** - * Attach a volume to an instance - * - * @param request - * @return - */ - public EC2Volume attachVolume( EC2Volume request ) { - try { - request.setDeviceId(mapDeviceToCloudDeviceId(request.getDevice())); - EC2Volume resp = new EC2Volume(); - - CloudStackVolume vol = getApi().attachVolume(request.getId(), request.getInstanceId(), request.getDeviceId()); - if(vol != null) { - resp.setAttached(vol.getAttached()); - resp.setCreated(vol.getCreated()); - resp.setDevice(request.getDevice()); - resp.setDeviceId(vol.getDeviceId()); - resp.setHypervisor(vol.getHypervisor()); - resp.setId(vol.getId()); - resp.setInstanceId(vol.getVirtualMachineId()); - resp.setSize(vol.getSize()); - resp.setSnapshotId(vol.getSnapshotId()); - resp.setState(vol.getState()); - resp.setType(vol.getVolumeType()); - resp.setVMState(vol.getVirtualMachineState()); - resp.setZoneName(vol.getZoneName()); - return resp; - } - throw new EC2ServiceException( ServerError.InternalError, "An unexpected error occurred." ); - } catch( Exception e ) { - logger.error( "EC2 AttachVolume 2 - ", e); - throw new EC2ServiceException( ServerError.InternalError, e.getMessage() != null ? e.getMessage() : e.toString()); - } - } + /** + * Attach a volume to an instance + * + * @param request + * @return + */ + public EC2Volume attachVolume( EC2Volume request ) { + try { + request.setDeviceId(mapDeviceToCloudDeviceId(request.getDevice())); + EC2Volume resp = new EC2Volume(); - /** - * Detach a volume from an instance - * - * @param request - * @return - */ - public EC2Volume detachVolume(EC2Volume request) { - try { - CloudStackVolume vol = getApi().detachVolume(null, request.getId(), null); - EC2Volume resp = new EC2Volume(); - - if(vol != null) { - resp.setAttached(vol.getAttached()); - resp.setCreated(vol.getCreated()); - resp.setDevice(request.getDevice()); - resp.setDeviceId(vol.getDeviceId()); - resp.setHypervisor(vol.getHypervisor()); - resp.setId(vol.getId()); - resp.setInstanceId(vol.getVirtualMachineId()); - resp.setSize(vol.getSize()); - resp.setSnapshotId(vol.getSnapshotId()); - resp.setState(vol.getState()); - resp.setType(vol.getVolumeType()); - resp.setVMState(vol.getVirtualMachineState()); - resp.setZoneName(vol.getZoneName()); - return resp; - } + CloudStackVolume vol = getApi().attachVolume(request.getId(), request.getInstanceId(), request.getDeviceId()); + if(vol != null) { + resp.setAttached(vol.getAttached()); + resp.setCreated(vol.getCreated()); + resp.setDevice(request.getDevice()); + resp.setDeviceId(vol.getDeviceId()); + resp.setHypervisor(vol.getHypervisor()); + resp.setId(vol.getId()); + resp.setInstanceId(vol.getVirtualMachineId()); + resp.setSize(vol.getSize()); + resp.setSnapshotId(vol.getSnapshotId()); + resp.setState(vol.getState()); + resp.setType(vol.getVolumeType()); + resp.setVMState(vol.getVirtualMachineState()); + resp.setZoneName(vol.getZoneName()); + return resp; + } + throw new EC2ServiceException( ServerError.InternalError, "An unexpected error occurred." ); + } catch( Exception e ) { + logger.error( "EC2 AttachVolume 2 - ", e); + throw new EC2ServiceException( ServerError.InternalError, e.getMessage() != null ? e.getMessage() : e.toString()); + } + } - throw new EC2ServiceException( ServerError.InternalError, "An unexpected error occurred." ); - } catch( Exception e ) { - logger.error( "EC2 DetachVolume - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + /** + * Detach a volume from an instance + * + * @param request + * @return + */ + public EC2Volume detachVolume(EC2Volume request) { + try { + CloudStackVolume vol = getApi().detachVolume(null, request.getId(), null); + EC2Volume resp = new EC2Volume(); - /** - * Create a volume - * - * @param request - * @return - */ - public EC2Volume createVolume( EC2CreateVolume request ) { - try { - - CloudStackAccount caller = getCurrentAccount(); - // -> put either snapshotid or diskofferingid on the request - String snapshotId = request.getSnapshotId(); - Long size = request.getSize(); - String diskOfferingId = null; + if(vol != null) { + resp.setAttached(vol.getAttached()); + resp.setCreated(vol.getCreated()); + resp.setDevice(request.getDevice()); + resp.setDeviceId(vol.getDeviceId()); + resp.setHypervisor(vol.getHypervisor()); + resp.setId(vol.getId()); + resp.setInstanceId(vol.getVirtualMachineId()); + resp.setSize(vol.getSize()); + resp.setSnapshotId(vol.getSnapshotId()); + resp.setState(vol.getState()); + resp.setType(vol.getVolumeType()); + resp.setVMState(vol.getVirtualMachineState()); + resp.setZoneName(vol.getZoneName()); + return resp; + } - if (snapshotId == null) { - List disks = getApi().listDiskOfferings(null, null, null, null); - for (CloudStackDiskOffering offer : disks) { - if (offer.isCustomized()) { - diskOfferingId = offer.getId(); - } - } - if (diskOfferingId == null) throw new EC2ServiceException(ServerError.InternalError, "No Customize Disk Offering Found"); - } + throw new EC2ServiceException( ServerError.InternalError, "An unexpected error occurred." ); + } catch( Exception e ) { + logger.error( "EC2 DetachVolume - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } + + /** + * Create a volume + * + * @param request + * @return + */ + public EC2Volume createVolume( EC2CreateVolume request ) { + try { + + CloudStackAccount caller = getCurrentAccount(); + // -> put either snapshotid or diskofferingid on the request + String snapshotId = request.getSnapshotId(); + Long size = request.getSize(); + String diskOfferingId = null; + + if (snapshotId == null) { + List disks = getApi().listDiskOfferings(null, null, null, null); + for (CloudStackDiskOffering offer : disks) { + if (offer.isCustomized()) { + diskOfferingId = offer.getId(); + } + } + if (diskOfferingId == null) throw new EC2ServiceException(ServerError.InternalError, "No Customize Disk Offering Found"); + } // // -> no volume name is given in the Amazon request but is required in the cloud API - CloudStackVolume vol = getApi().createVolume(UUID.randomUUID().toString(), null, diskOfferingId, null, size, snapshotId, toZoneId(request.getZoneName(), null)); - if (vol != null) { - EC2Volume resp = new EC2Volume(); - resp.setAttached(vol.getAttached()); - resp.setCreated(vol.getCreated()); + CloudStackVolume vol = getApi().createVolume(UUID.randomUUID().toString(), null, diskOfferingId, null, size, snapshotId, toZoneId(request.getZoneName(), null)); + if (vol != null) { + EC2Volume resp = new EC2Volume(); + resp.setAttached(vol.getAttached()); + resp.setCreated(vol.getCreated()); // resp.setDevice(); - resp.setDeviceId(vol.getDeviceId()); - resp.setHypervisor(vol.getHypervisor()); - resp.setId(vol.getId()); - resp.setInstanceId(vol.getVirtualMachineId()); - resp.setSize(vol.getSize()); - resp.setSnapshotId(vol.getSnapshotId()); - resp.setState(vol.getState()); - resp.setType(vol.getVolumeType()); - resp.setVMState(vol.getVirtualMachineState()); - resp.setZoneName(vol.getZoneName()); - return resp; - } - return null; - } catch( Exception e ) { - logger.error( "EC2 CreateVolume - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + resp.setDeviceId(vol.getDeviceId()); + resp.setHypervisor(vol.getHypervisor()); + resp.setId(vol.getId()); + resp.setInstanceId(vol.getVirtualMachineId()); + resp.setSize(vol.getSize()); + resp.setSnapshotId(vol.getSnapshotId()); + resp.setState(vol.getState()); + resp.setType(vol.getVolumeType()); + resp.setVMState(vol.getVirtualMachineState()); + resp.setZoneName(vol.getZoneName()); + return resp; + } + return null; + } catch( Exception e ) { + logger.error( "EC2 CreateVolume - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } - /** - * Delete a volume - * - * @param request - * @return - */ - public EC2Volume deleteVolume( EC2Volume request ) { - try { - CloudStackInfoResponse resp = getApi().deleteVolume(request.getId()); - if(resp != null) { - request.setState("deleted"); - return request; - } + /** + * Delete a volume + * + * @param request + * @return + */ + public EC2Volume deleteVolume( EC2Volume request ) { + try { + CloudStackInfoResponse resp = getApi().deleteVolume(request.getId()); + if(resp != null) { + request.setState("deleted"); + return request; + } - throw new EC2ServiceException(ServerError.InternalError, "An unexpected error occurred."); - } catch( Exception e ) { - logger.error( "EC2 DeleteVolume 2 - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + throw new EC2ServiceException(ServerError.InternalError, "An unexpected error occurred."); + } catch( Exception e ) { + logger.error( "EC2 DeleteVolume 2 - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } /** * Create/Delete tags @@ -1395,7 +1386,7 @@ public class EC2Engine { if (resourceTag.getValue() != null) tag.setValue(resourceTag.getValue()); tagResponse.addTags(tag); - } + } } EC2TagsFilterSet tfs = request.getFilterSet(); @@ -1409,116 +1400,116 @@ public class EC2Engine { } } - /** - * Reboot an instance or instances - * - * @param request - * @return - */ - public boolean rebootInstances(EC2RebootInstances request) - { - EC2Instance[] vms = null; + /** + * Reboot an instance or instances + * + * @param request + * @return + */ + public boolean rebootInstances(EC2RebootInstances request) + { + EC2Instance[] vms = null; - // -> reboot is not allowed on destroyed (i.e., terminated) instances - try { - String[] instanceSet = request.getInstancesSet(); - EC2DescribeInstancesResponse previousState = listVirtualMachines( instanceSet, null, null ); - vms = previousState.getInstanceSet(); + // -> reboot is not allowed on destroyed (i.e., terminated) instances + try { + String[] instanceSet = request.getInstancesSet(); + EC2DescribeInstancesResponse previousState = listVirtualMachines( instanceSet, null, null ); + vms = previousState.getInstanceSet(); - // -> send reboot requests for each found VM - for (EC2Instance vm : vms) { - if (vm.getState().equalsIgnoreCase( "Destroyed" )) continue; - - CloudStackUserVm resp = getApi().rebootVirtualMachine(vm.getId()); - if (logger.isDebugEnabled()) - logger.debug("Rebooting VM " + resp.getId() + " job " + resp.getJobId()); - } + // -> send reboot requests for each found VM + for (EC2Instance vm : vms) { + if (vm.getState().equalsIgnoreCase( "Destroyed" )) continue; - // -> if some specified VMs where not found we have to tell the caller - if (instanceSet.length != vms.length) - throw new EC2ServiceException(ClientError.InvalidAMIID_NotFound, "One or more instanceIds do not exist, other instances rebooted."); + CloudStackUserVm resp = getApi().rebootVirtualMachine(vm.getId()); + if (logger.isDebugEnabled()) + logger.debug("Rebooting VM " + resp.getId() + " job " + resp.getJobId()); + } - return true; - } catch( Exception e ) { - logger.error( "EC2 RebootInstances - ", e ); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + // -> if some specified VMs where not found we have to tell the caller + if (instanceSet.length != vms.length) + throw new EC2ServiceException(ClientError.InvalidAMIID_NotFound, "One or more instanceIds do not exist, other instances rebooted."); - /** - * Using a template (AMI), launch n instances - * - * @param request - * @return - */ - public EC2RunInstancesResponse runInstances(EC2RunInstances request) { - EC2RunInstancesResponse instances = new EC2RunInstancesResponse(); - int createInstances = 0; - int canCreateInstances = -1; - int countCreated = 0; + return true; + } catch( Exception e ) { + logger.error( "EC2 RebootInstances - ", e ); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } - try { - CloudStackAccount caller = getCurrentAccount(); - - // ugly... - canCreateInstances = calculateAllowedInstances(); - if (-1 == canCreateInstances) canCreateInstances = request.getMaxCount(); + /** + * Using a template (AMI), launch n instances + * + * @param request + * @return + */ + public EC2RunInstancesResponse runInstances(EC2RunInstances request) { + EC2RunInstancesResponse instances = new EC2RunInstancesResponse(); + int createInstances = 0; + int canCreateInstances = -1; + int countCreated = 0; - if (canCreateInstances < request.getMinCount()) { - logger.info( "EC2 RunInstances - min count too big (" + request.getMinCount() + "), " + canCreateInstances + " left to allocate"); - throw new EC2ServiceException(ClientError.InstanceLimitExceeded ,"Only " + canCreateInstances + " instance(s) left to allocate"); - } + try { + CloudStackAccount caller = getCurrentAccount(); - if ( canCreateInstances < request.getMaxCount()) - createInstances = request.getMinCount(); - else - createInstances = request.getMaxCount(); + // ugly... + canCreateInstances = calculateAllowedInstances(); + if (-1 == canCreateInstances) canCreateInstances = request.getMaxCount(); - //find CS service Offering ID - String instanceType = "m1.small"; - if(request.getInstanceType() != null){ - instanceType = request.getInstanceType(); - } - CloudStackServiceOfferingVO svcOffering = getCSServiceOfferingId(instanceType); - if(svcOffering == null){ - logger.info("No ServiceOffering found to be defined by name, please contact the administrator "+instanceType ); - throw new EC2ServiceException(ClientError.Unsupported, "instanceType: [" + instanceType + "] not found!"); - } - - // zone stuff - String zoneId = toZoneId(request.getZoneName(), null); - - List zones = getApi().listZones(null, null, zoneId, null); - if (zones == null || zones.size() == 0) { - logger.info("EC2 RunInstances - zone [" + request.getZoneName() + "] not found!"); - throw new EC2ServiceException(ClientError.InvalidZone_NotFound, "ZoneId [" + request.getZoneName() + "] not found!"); - } - // we choose first zone? - CloudStackZone zone = zones.get(0); + if (canCreateInstances < request.getMinCount()) { + logger.info( "EC2 RunInstances - min count too big (" + request.getMinCount() + "), " + canCreateInstances + " left to allocate"); + throw new EC2ServiceException(ClientError.InstanceLimitExceeded ,"Only " + canCreateInstances + " instance(s) left to allocate"); + } - // network - CloudStackNetwork network = findNetwork(zone); + if ( canCreateInstances < request.getMaxCount()) + createInstances = request.getMinCount(); + else + createInstances = request.getMaxCount(); - // now actually deploy the vms - for( int i=0; i < createInstances; i++ ) { - CloudStackUserVm resp = getApi().deployVirtualMachine(svcOffering.getId(), - request.getTemplateId(), zoneId, null, null, null, null, - null, null, null, request.getKeyName(), null, (network != null ? network.getId() : null), - null, constructList(request.getGroupSet()), request.getSize().longValue(), request.getUserData()); - EC2Instance vm = new EC2Instance(); - vm.setId(resp.getId().toString()); - vm.setName(resp.getName()); - vm.setZoneName(resp.getZoneName()); - vm.setTemplateId(resp.getTemplateId().toString()); - if (resp.getSecurityGroupList() != null && resp.getSecurityGroupList().size() > 0) { - // TODO, we have a list of security groups, just return the first one? + //find CS service Offering ID + String instanceType = "m1.small"; + if(request.getInstanceType() != null){ + instanceType = request.getInstanceType(); + } + CloudStackServiceOfferingVO svcOffering = getCSServiceOfferingId(instanceType); + if(svcOffering == null){ + logger.info("No ServiceOffering found to be defined by name, please contact the administrator "+instanceType ); + throw new EC2ServiceException(ClientError.Unsupported, "instanceType: [" + instanceType + "] not found!"); + } + + // zone stuff + String zoneId = toZoneId(request.getZoneName(), null); + + List zones = getApi().listZones(null, null, zoneId, null); + if (zones == null || zones.size() == 0) { + logger.info("EC2 RunInstances - zone [" + request.getZoneName() + "] not found!"); + throw new EC2ServiceException(ClientError.InvalidZone_NotFound, "ZoneId [" + request.getZoneName() + "] not found!"); + } + // we choose first zone? + CloudStackZone zone = zones.get(0); + + // network + CloudStackNetwork network = findNetwork(zone); + + // now actually deploy the vms + for( int i=0; i < createInstances; i++ ) { + CloudStackUserVm resp = getApi().deployVirtualMachine(svcOffering.getId(), + request.getTemplateId(), zoneId, null, null, null, null, + null, null, null, request.getKeyName(), null, (network != null ? network.getId() : null), + null, constructList(request.getGroupSet()), request.getSize().longValue(), request.getUserData()); + EC2Instance vm = new EC2Instance(); + vm.setId(resp.getId().toString()); + vm.setName(resp.getName()); + vm.setZoneName(resp.getZoneName()); + vm.setTemplateId(resp.getTemplateId().toString()); + if (resp.getSecurityGroupList() != null && resp.getSecurityGroupList().size() > 0) { + // TODO, we have a list of security groups, just return the first one? List securityGroupList = resp.getSecurityGroupList(); for (CloudStackSecurityGroup securityGroup : securityGroupList) { vm.addGroupName(securityGroup.getName()); } } - vm.setState(resp.getState()); - vm.setCreated(resp.getCreated()); + vm.setState(resp.getState()); + vm.setCreated(resp.getCreated()); List nicList = resp.getNics(); for (CloudStackNic nic : nicList) { if (nic.getIsDefault()) { @@ -1527,215 +1518,215 @@ public class EC2Engine { } } vm.setIpAddress(resp.getIpAddress()); - vm.setAccountName(resp.getAccountName()); - vm.setDomainId(resp.getDomainId()); - vm.setHypervisor(resp.getHypervisor()); - vm.setServiceOffering( svcOffering.getName()); + vm.setAccountName(resp.getAccountName()); + vm.setDomainId(resp.getDomainId()); + vm.setHypervisor(resp.getHypervisor()); + vm.setServiceOffering( svcOffering.getName()); vm.setKeyPairName(resp.getKeyPairName()); - instances.addInstance(vm); - countCreated++; - } + instances.addInstance(vm); + countCreated++; + } - if (0 == countCreated) { - // TODO, we actually need to destroy left-over VMs when the exception is thrown - throw new EC2ServiceException(ServerError.InsufficientInstanceCapacity, "Insufficient Instance Capacity" ); - } + if (0 == countCreated) { + // TODO, we actually need to destroy left-over VMs when the exception is thrown + throw new EC2ServiceException(ServerError.InsufficientInstanceCapacity, "Insufficient Instance Capacity" ); + } - return instances; - } catch( Exception e ) { - logger.error( "EC2 RunInstances - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + return instances; + } catch( Exception e ) { + logger.error( "EC2 RunInstances - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } - /** - * Start an instance or instances - * - * @param request - * @return - */ - public EC2StartInstancesResponse startInstances(EC2StartInstances request) { - EC2StartInstancesResponse instances = new EC2StartInstancesResponse(); - EC2Instance[] vms = null; + /** + * Start an instance or instances + * + * @param request + * @return + */ + public EC2StartInstancesResponse startInstances(EC2StartInstances request) { + EC2StartInstancesResponse instances = new EC2StartInstancesResponse(); + EC2Instance[] vms = null; - // -> first determine the current state of each VM (becomes it previous state) - try { - EC2DescribeInstancesResponse previousState = listVirtualMachines( request.getInstancesSet(), null, null ); - vms = previousState.getInstanceSet(); + // -> first determine the current state of each VM (becomes it previous state) + try { + EC2DescribeInstancesResponse previousState = listVirtualMachines( request.getInstancesSet(), null, null ); + vms = previousState.getInstanceSet(); - // -> send start requests for each item - for (EC2Instance vm : vms) { - vm.setPreviousState(vm.getState()); + // -> send start requests for each item + for (EC2Instance vm : vms) { + vm.setPreviousState(vm.getState()); - // -> if its already running then we don't care - if (vm.getState().equalsIgnoreCase( "Running" ) || vm.getState().equalsIgnoreCase( "Destroyed" )) { - instances.addInstance(vm); - continue; - } + // -> if its already running then we don't care + if (vm.getState().equalsIgnoreCase( "Running" ) || vm.getState().equalsIgnoreCase( "Destroyed" )) { + instances.addInstance(vm); + continue; + } - CloudStackUserVm resp = getApi().startVirtualMachine(vm.getId()); - if(resp != null){ - vm.setState(resp.getState()); - if(logger.isDebugEnabled()) - logger.debug("Starting VM " + vm.getId() + " job " + resp.getJobId()); - } - instances.addInstance(vm); - } - return instances; - } catch( Exception e ) { - logger.error( "EC2 StartInstances - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + CloudStackUserVm resp = getApi().startVirtualMachine(vm.getId()); + if(resp != null){ + vm.setState(resp.getState()); + if(logger.isDebugEnabled()) + logger.debug("Starting VM " + vm.getId() + " job " + resp.getJobId()); + } + instances.addInstance(vm); + } + return instances; + } catch( Exception e ) { + logger.error( "EC2 StartInstances - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } - /** - * Stop an instance or instances - * - * @param request - * @return - */ - public EC2StopInstancesResponse stopInstances(EC2StopInstances request) { - EC2StopInstancesResponse instances = new EC2StopInstancesResponse(); - EC2Instance[] virtualMachines = null; + /** + * Stop an instance or instances + * + * @param request + * @return + */ + public EC2StopInstancesResponse stopInstances(EC2StopInstances request) { + EC2StopInstancesResponse instances = new EC2StopInstancesResponse(); + EC2Instance[] virtualMachines = null; - // -> first determine the current state of each VM (becomes it previous state) - try { - String[] instanceSet = request.getInstancesSet(); + // -> first determine the current state of each VM (becomes it previous state) + try { + String[] instanceSet = request.getInstancesSet(); - EC2DescribeInstancesResponse previousState = listVirtualMachines( instanceSet, null, null ); - virtualMachines = previousState.getInstanceSet(); + EC2DescribeInstancesResponse previousState = listVirtualMachines( instanceSet, null, null ); + virtualMachines = previousState.getInstanceSet(); - // -> send stop requests for each item - for (EC2Instance vm : virtualMachines) { - vm.setPreviousState( vm.getState()); - CloudStackUserVm resp = null; - if (request.getDestroyInstances()) { - if (vm.getState().equalsIgnoreCase( "Destroyed" )) { - instances.addInstance(vm); - continue; - } - resp = getApi().destroyVirtualMachine(vm.getId()); - if(logger.isDebugEnabled()) - logger.debug("Destroying VM " + vm.getId() + " job " + resp.getJobId()); - } else { - if (vm.getState().equalsIgnoreCase("Stopped") || vm.getState().equalsIgnoreCase("Destroyed")) { - instances.addInstance(vm); - continue; - } - resp = getApi().stopVirtualMachine(vm.getId(), false); - if(logger.isDebugEnabled()) - logger.debug("Stopping VM " + vm.getId() + " job " + resp.getJobId()); - } - if (resp != null) { - vm.setState(resp.getState()); - instances.addInstance(vm); - } - } - return instances; - } catch( Exception e ) { - logger.error( "EC2 StopInstances - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() + ", might already be destroyed" : "An unexpected error occurred."); - } - } + // -> send stop requests for each item + for (EC2Instance vm : virtualMachines) { + vm.setPreviousState( vm.getState()); + CloudStackUserVm resp = null; + if (request.getDestroyInstances()) { + if (vm.getState().equalsIgnoreCase( "Destroyed" )) { + instances.addInstance(vm); + continue; + } + resp = getApi().destroyVirtualMachine(vm.getId()); + if(logger.isDebugEnabled()) + logger.debug("Destroying VM " + vm.getId() + " job " + resp.getJobId()); + } else { + if (vm.getState().equalsIgnoreCase("Stopped") || vm.getState().equalsIgnoreCase("Destroyed")) { + instances.addInstance(vm); + continue; + } + resp = getApi().stopVirtualMachine(vm.getId(), false); + if(logger.isDebugEnabled()) + logger.debug("Stopping VM " + vm.getId() + " job " + resp.getJobId()); + } + if (resp != null) { + vm.setState(resp.getState()); + instances.addInstance(vm); + } + } + return instances; + } catch( Exception e ) { + logger.error( "EC2 StopInstances - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() + ", might already be destroyed" : "An unexpected error occurred."); + } + } - /** - * RunInstances includes a min and max count of requested instances to create. - * We have to be able to create the min number for the user or none at all. So - * here we determine what the user has left to create. - * - * @return -1 means no limit exists, other positive numbers give max number left that - * the user can create. - */ - private int calculateAllowedInstances() throws Exception { - int maxAllowed = -1; - - CloudStackAccount ourAccount = getCurrentAccount(); - - if (ourAccount == null) { - // This should never happen, but - // we will return -99999 if this happens... - return -99999; - } - - // if accountType is Admin == 1, then let's return -1 - if (ourAccount.getAccountType() == 1) return -1; - - // -> get the user limits on instances - // "0" represents instances: - // http://download.cloud.com/releases/2.2.0/api_2.2.8/user/listResourceLimits.html - List limits = getApi().listResourceLimits(null, null, null, null, "0"); - if (limits != null && limits.size() > 0) { - maxAllowed = (int)limits.get(0).getMax().longValue(); - if (maxAllowed == -1) - return -1; // no limit + /** + * RunInstances includes a min and max count of requested instances to create. + * We have to be able to create the min number for the user or none at all. So + * here we determine what the user has left to create. + * + * @return -1 means no limit exists, other positive numbers give max number left that + * the user can create. + */ + private int calculateAllowedInstances() throws Exception { + int maxAllowed = -1; - EC2DescribeInstancesResponse existingVMS = listVirtualMachines( null, null, null ); - EC2Instance[] vmsList = existingVMS.getInstanceSet(); - return (maxAllowed - vmsList.length); - } else { - return 0; - } - } + CloudStackAccount ourAccount = getCurrentAccount(); - /** - * Performs the cloud API listVirtualMachines one or more times. - * - * @param virtualMachineIds - an array of instances we are interested in getting information on - * @param ifs - filter out unwanted instances - */ - private EC2DescribeInstancesResponse listVirtualMachines( String[] virtualMachineIds, EC2InstanceFilterSet ifs, + if (ourAccount == null) { + // This should never happen, but + // we will return -99999 if this happens... + return -99999; + } + + // if accountType is Admin == 1, then let's return -1 + if (ourAccount.getAccountType() == 1) return -1; + + // -> get the user limits on instances + // "0" represents instances: + // http://download.cloud.com/releases/2.2.0/api_2.2.8/user/listResourceLimits.html + List limits = getApi().listResourceLimits(null, null, null, null, "0"); + if (limits != null && limits.size() > 0) { + maxAllowed = (int)limits.get(0).getMax().longValue(); + if (maxAllowed == -1) + return -1; // no limit + + EC2DescribeInstancesResponse existingVMS = listVirtualMachines( null, null, null ); + EC2Instance[] vmsList = existingVMS.getInstanceSet(); + return (maxAllowed - vmsList.length); + } else { + return 0; + } + } + + /** + * Performs the cloud API listVirtualMachines one or more times. + * + * @param virtualMachineIds - an array of instances we are interested in getting information on + * @param ifs - filter out unwanted instances + */ + private EC2DescribeInstancesResponse listVirtualMachines( String[] virtualMachineIds, EC2InstanceFilterSet ifs, List resourceTags ) throws Exception - { - EC2DescribeInstancesResponse instances = new EC2DescribeInstancesResponse(); + { + EC2DescribeInstancesResponse instances = new EC2DescribeInstancesResponse(); - if (null == virtualMachineIds || 0 == virtualMachineIds.length) { + if (null == virtualMachineIds || 0 == virtualMachineIds.length) { instances = lookupInstances( null, instances, resourceTags ); - } else { - for( int i=0; i < virtualMachineIds.length; i++ ) { + } else { + for( int i=0; i < virtualMachineIds.length; i++ ) { instances = lookupInstances( virtualMachineIds[i], instances, resourceTags ); - } - } + } + } - if ( null == ifs ) - return instances; - else return ifs.evaluate( instances ); - } + if ( null == ifs ) + return instances; + else return ifs.evaluate( instances ); + } - /** - * Get one or more templates depending on the volumeId parameter. - * - * @param volumeId - if interested in one specific volume, null if want to list all volumes - * @param instanceId - if interested in volumes for a specific instance, null if instance is not important - */ - private EC2DescribeVolumesResponse listVolumes(String volumeId, String instanceId, EC2DescribeVolumesResponse volumes, + /** + * Get one or more templates depending on the volumeId parameter. + * + * @param volumeId - if interested in one specific volume, null if want to list all volumes + * @param instanceId - if interested in volumes for a specific instance, null if instance is not important + */ + private EC2DescribeVolumesResponse listVolumes(String volumeId, String instanceId, EC2DescribeVolumesResponse volumes, List resourceTagSet)throws Exception { List vols = getApi().listVolumes(null, null, null, volumeId, null, null, null, null, null, instanceId, null, resourceTagSet); - if(vols != null && vols.size() > 0) { - for(CloudStackVolume vol : vols) { - EC2Volume ec2Vol = new EC2Volume(); - ec2Vol.setId(vol.getId()); - if(vol.getAttached() != null) - ec2Vol.setAttached(vol.getAttached()); - ec2Vol.setCreated(vol.getCreated()); + if(vols != null && vols.size() > 0) { + for(CloudStackVolume vol : vols) { + EC2Volume ec2Vol = new EC2Volume(); + ec2Vol.setId(vol.getId()); + if(vol.getAttached() != null) + ec2Vol.setAttached(vol.getAttached()); + ec2Vol.setCreated(vol.getCreated()); - if(vol.getDeviceId() != null) - ec2Vol.setDeviceId(vol.getDeviceId()); - ec2Vol.setHypervisor(vol.getHypervisor()); + if(vol.getDeviceId() != null) + ec2Vol.setDeviceId(vol.getDeviceId()); + ec2Vol.setHypervisor(vol.getHypervisor()); - if(vol.getSnapshotId() != null) - ec2Vol.setSnapshotId(vol.getSnapshotId()); - ec2Vol.setState(mapToAmazonVolState(vol.getState())); - ec2Vol.setSize(vol.getSize()); - ec2Vol.setType(vol.getVolumeType()); + if(vol.getSnapshotId() != null) + ec2Vol.setSnapshotId(vol.getSnapshotId()); + ec2Vol.setState(mapToAmazonVolState(vol.getState())); + ec2Vol.setSize(vol.getSize()); + ec2Vol.setType(vol.getVolumeType()); - if(vol.getVirtualMachineId() != null) - ec2Vol.setInstanceId(vol.getVirtualMachineId()); + if(vol.getVirtualMachineId() != null) + ec2Vol.setInstanceId(vol.getVirtualMachineId()); - if(vol.getVirtualMachineState() != null) - ec2Vol.setVMState(vol.getVirtualMachineState()); - ec2Vol.setZoneName(vol.getZoneName()); + if(vol.getVirtualMachineState() != null) + ec2Vol.setVMState(vol.getVirtualMachineState()); + ec2Vol.setZoneName(vol.getZoneName()); List resourceTags = vol.getTags(); for(CloudStackKeyValue resourceTag : resourceTags) { @@ -1746,72 +1737,72 @@ public class EC2Engine { ec2Vol.addResourceTag(param); } - volumes.addVolume(ec2Vol); - } - } + volumes.addVolume(ec2Vol); + } + } - return volumes; - } + return volumes; + } - /** - * Translate the given zone name into the required zoneId. Query for - * a list of all zones and match the zone name given. Amazon uses zone - * names while the Cloud API often requires the zoneId. - * - * @param zoneName - (e.g., 'AH'), if null return the first zone in the available list - * - * @return the zoneId that matches the given zone name - */ - private String toZoneId(String zoneName, String domainId) throws Exception { - EC2DescribeAvailabilityZonesResponse zones = null; - String[] interestedZones = null; - - if ( null != zoneName) { - interestedZones = new String[1]; - interestedZones[0] = zoneName; - }else { - CloudStackZone zone = findZone(); - if(zone != null){ - return zone.getId(); - } - } - - zones = listZones(interestedZones, domainId); - - if (zones == null || zones.getZoneIdAt( 0 ) == null) - throw new EC2ServiceException(ClientError.InvalidParameterValue, "Unknown zoneName value - " + zoneName); - return zones.getZoneIdAt(0); - } - - - /** - * Convert from the Amazon instanceType strings to Cloud serviceOfferingId + /** + * Translate the given zone name into the required zoneId. Query for + * a list of all zones and match the zone name given. Amazon uses zone + * names while the Cloud API often requires the zoneId. * - */ - - private CloudStackServiceOfferingVO getCSServiceOfferingId(String instanceType){ - try { - if (null == instanceType) instanceType = "m1.small"; - - return scvoDao.getSvcOfferingByName(instanceType); - + * @param zoneName - (e.g., 'AH'), if null return the first zone in the available list + * + * @return the zoneId that matches the given zone name + */ + private String toZoneId(String zoneName, String domainId) throws Exception { + EC2DescribeAvailabilityZonesResponse zones = null; + String[] interestedZones = null; + + if ( null != zoneName) { + interestedZones = new String[1]; + interestedZones[0] = zoneName; + }else { + CloudStackZone zone = findZone(); + if(zone != null){ + return zone.getId(); + } + } + + zones = listZones(interestedZones, domainId); + + if (zones == null || zones.getZoneIdAt( 0 ) == null) + throw new EC2ServiceException(ClientError.InvalidParameterValue, "Unknown zoneName value - " + zoneName); + return zones.getZoneIdAt(0); + } + + + /** + * Convert from the Amazon instanceType strings to Cloud serviceOfferingId + * + */ + + private CloudStackServiceOfferingVO getCSServiceOfferingId(String instanceType){ + try { + if (null == instanceType) instanceType = "m1.small"; + + return scvoDao.getSvcOfferingByName(instanceType); + } catch(Exception e) { logger.error( "Error while retrieving ServiceOffering information by name - ", e); throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); } - } - - /** - * Convert from the Cloud serviceOfferingId to the Amazon instanceType strings based - * on the loaded map. - * - * @param serviceOfferingId - * @return A valid value for the Amazon defined instanceType - * @throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException - */ - private String serviceOfferingIdToInstanceType( String serviceOfferingId ){ + } + + /** + * Convert from the Cloud serviceOfferingId to the Amazon instanceType strings based + * on the loaded map. + * + * @param serviceOfferingId + * @return A valid value for the Amazon defined instanceType + * @throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException + */ + private String serviceOfferingIdToInstanceType( String serviceOfferingId ){ try{ - + CloudStackServiceOfferingVO offering = scvoDao.getSvcOfferingById(serviceOfferingId); //dao.getSvcOfferingById(serviceOfferingId); if(offering == null){ logger.warn( "No instanceType match for serviceOfferingId: [" + serviceOfferingId + "]" ); @@ -1823,107 +1814,107 @@ public class EC2Engine { logger.error( "sError while retrieving ServiceOffering information by id - ", e); throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); } - } + } - /** - * Match the value in the 'description' field of the listOsTypes response to get - * the osTypeId. - * - * @param osTypeName - * @return the Cloud.com API osTypeId - */ - private String toOSTypeId( String osTypeName ) throws Exception { - try { - List osTypes = getApi().listOsTypes(null, null, null); - for (CloudStackOsType osType : osTypes) { - if (osType.getDescription().toLowerCase().indexOf(osTypeName.toLowerCase()) != -1) - return osType.getId(); - } - return null; - } catch(Exception e) { - logger.error( "List OS Types - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } + /** + * Match the value in the 'description' field of the listOsTypes response to get + * the osTypeId. + * + * @param osTypeName + * @return the Cloud.com API osTypeId + */ + private String toOSTypeId( String osTypeName ) throws Exception { + try { + List osTypes = getApi().listOsTypes(null, null, null); + for (CloudStackOsType osType : osTypes) { + if (osType.getDescription().toLowerCase().indexOf(osTypeName.toLowerCase()) != -1) + return osType.getId(); + } + return null; + } catch(Exception e) { + logger.error( "List OS Types - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } - } + } - /** - * More than one place we need to access the defined list of zones. If given a specific - * list of zones of interest, then only values from those zones are returned. - * - * @param interestedZones - can be null, should be a subset of all zones - * - * @return EC2DescribeAvailabilityZonesResponse - */ - private EC2DescribeAvailabilityZonesResponse listZones(String[] interestedZones, String domainId) throws Exception - { - EC2DescribeAvailabilityZonesResponse zones = new EC2DescribeAvailabilityZonesResponse(); + /** + * More than one place we need to access the defined list of zones. If given a specific + * list of zones of interest, then only values from those zones are returned. + * + * @param interestedZones - can be null, should be a subset of all zones + * + * @return EC2DescribeAvailabilityZonesResponse + */ + private EC2DescribeAvailabilityZonesResponse listZones(String[] interestedZones, String domainId) throws Exception + { + EC2DescribeAvailabilityZonesResponse zones = new EC2DescribeAvailabilityZonesResponse(); - List cloudZones = getApi().listZones(true, domainId, null, null); + List cloudZones = getApi().listZones(true, domainId, null, null); - if(cloudZones != null) { - for(CloudStackZone cloudZone : cloudZones) { - if ( null != interestedZones && 0 < interestedZones.length ) { - for( int j=0; j < interestedZones.length; j++ ) { - if (interestedZones[j].equalsIgnoreCase( cloudZone.getName())) { - zones.addZone(cloudZone.getId().toString(), cloudZone.getName()); - break; - } - } - } else { - zones.addZone(cloudZone.getId().toString(), cloudZone.getName()); - } - } - } - return zones; - } + if(cloudZones != null) { + for(CloudStackZone cloudZone : cloudZones) { + if ( null != interestedZones && 0 < interestedZones.length ) { + for( int j=0; j < interestedZones.length; j++ ) { + if (interestedZones[j].equalsIgnoreCase( cloudZone.getName())) { + zones.addZone(cloudZone.getId().toString(), cloudZone.getName()); + break; + } + } + } else { + zones.addZone(cloudZone.getId().toString(), cloudZone.getName()); + } + } + } + return zones; + } - /** - * Get information on one or more virtual machines depending on the instanceId parameter. - * - * @param instanceId - if null then return information on all existing instances, otherwise - * just return information on the matching instance. - * @param instances - a container object to fill with one or more EC2Instance objects - * - * @return the same object passed in as the "instances" parameter modified with one or more - * EC2Instance objects loaded. - */ - private EC2DescribeInstancesResponse lookupInstances( String instanceId, EC2DescribeInstancesResponse instances, + /** + * Get information on one or more virtual machines depending on the instanceId parameter. + * + * @param instanceId - if null then return information on all existing instances, otherwise + * just return information on the matching instance. + * @param instances - a container object to fill with one or more EC2Instance objects + * + * @return the same object passed in as the "instances" parameter modified with one or more + * EC2Instance objects loaded. + */ + private EC2DescribeInstancesResponse lookupInstances( String instanceId, EC2DescribeInstancesResponse instances, List resourceTagSet ) - throws Exception { + throws Exception { - String instId = instanceId != null ? instanceId : null; + String instId = instanceId != null ? instanceId : null; List vms = getApi().listVirtualMachines(null, null, true, null, null, null, null, - instId, null, null, null, null, null, null, null, null, resourceTagSet); - - if(vms != null && vms.size() > 0) { - for(CloudStackUserVm cloudVm : vms) { - EC2Instance ec2Vm = new EC2Instance(); - - ec2Vm.setId(cloudVm.getId().toString()); - ec2Vm.setName(cloudVm.getName()); - ec2Vm.setZoneName(cloudVm.getZoneName()); - ec2Vm.setTemplateId(cloudVm.getTemplateId().toString()); - ec2Vm.setGroup(cloudVm.getGroup()); - ec2Vm.setState(cloudVm.getState()); - ec2Vm.setCreated(cloudVm.getCreated()); - ec2Vm.setIpAddress(cloudVm.getIpAddress()); - ec2Vm.setAccountName(cloudVm.getAccountName()); - ec2Vm.setDomainId(cloudVm.getDomainId()); - ec2Vm.setHypervisor(cloudVm.getHypervisor()); - ec2Vm.setRootDeviceType(cloudVm.getRootDeviceType()); - ec2Vm.setRootDeviceId(cloudVm.getRootDeviceId()); - ec2Vm.setServiceOffering(serviceOfferingIdToInstanceType(cloudVm.getServiceOfferingId().toString())); + instId, null, null, null, null, null, null, null, null, resourceTagSet); + + if(vms != null && vms.size() > 0) { + for(CloudStackUserVm cloudVm : vms) { + EC2Instance ec2Vm = new EC2Instance(); + + ec2Vm.setId(cloudVm.getId().toString()); + ec2Vm.setName(cloudVm.getName()); + ec2Vm.setZoneName(cloudVm.getZoneName()); + ec2Vm.setTemplateId(cloudVm.getTemplateId().toString()); + ec2Vm.setGroup(cloudVm.getGroup()); + ec2Vm.setState(cloudVm.getState()); + ec2Vm.setCreated(cloudVm.getCreated()); + ec2Vm.setIpAddress(cloudVm.getIpAddress()); + ec2Vm.setAccountName(cloudVm.getAccountName()); + ec2Vm.setDomainId(cloudVm.getDomainId()); + ec2Vm.setHypervisor(cloudVm.getHypervisor()); + ec2Vm.setRootDeviceType(cloudVm.getRootDeviceType()); + ec2Vm.setRootDeviceId(cloudVm.getRootDeviceId()); + ec2Vm.setServiceOffering(serviceOfferingIdToInstanceType(cloudVm.getServiceOfferingId().toString())); ec2Vm.setKeyPairName(cloudVm.getKeyPairName()); - List nics = cloudVm.getNics(); - for(CloudStackNic nic : nics) { - if(nic.getIsDefault()) { - ec2Vm.setPrivateIpAddress(nic.getIpaddress()); - break; - } - } + List nics = cloudVm.getNics(); + for(CloudStackNic nic : nics) { + if(nic.getIsDefault()) { + ec2Vm.setPrivateIpAddress(nic.getIpaddress()); + break; + } + } List resourceTags = cloudVm.getTags(); for(CloudStackKeyValue resourceTag : resourceTags) { @@ -1941,71 +1932,71 @@ public class EC2Engine { ec2Vm.addGroupName(securityGroup.getName()); } } - - instances.addInstance(ec2Vm); - } - }else{ - if(instanceId != null){ - //no such instance found - throw new EC2ServiceException(ServerError.InternalError, "Instance:" + instanceId + " not found"); - } - } - return instances; - } + + instances.addInstance(ec2Vm); + } + }else{ + if(instanceId != null){ + //no such instance found + throw new EC2ServiceException(ServerError.InternalError, "Instance:" + instanceId + " not found"); + } + } + return instances; + } - /** - * Get one or more templates depending on the templateId parameter. - * - * @param templateId - if null then return information on all existing templates, otherwise - * just return information on the matching template. - * @param images - a container object to fill with one or more EC2Image objects - * - * @return the same object passed in as the "images" parameter modified with one or more - * EC2Image objects loaded. - */ - private EC2DescribeImagesResponse listTemplates( String templateId, EC2DescribeImagesResponse images ) throws EC2ServiceException { - try { - List result = new ArrayList(); - - if(templateId != null){ + /** + * Get one or more templates depending on the templateId parameter. + * + * @param templateId - if null then return information on all existing templates, otherwise + * just return information on the matching template. + * @param images - a container object to fill with one or more EC2Image objects + * + * @return the same object passed in as the "images" parameter modified with one or more + * EC2Image objects loaded. + */ + private EC2DescribeImagesResponse listTemplates( String templateId, EC2DescribeImagesResponse images ) throws EC2ServiceException { + try { + List result = new ArrayList(); + + if(templateId != null){ List template = getApi().listTemplates("executable", null, null, null, templateId , null, null, null); if(template != null){ result.addAll(template); } - }else{ - List selfExecutable = getApi().listTemplates("selfexecutable", null, null, null, null, null, null, null); + }else{ + List selfExecutable = getApi().listTemplates("selfexecutable", null, null, null, null, null, null, null); if(selfExecutable != null){ result.addAll(selfExecutable); } - + List featured = getApi().listTemplates("featured", null, null, null, null, null, null, null); - if(featured != null){ - result.addAll(featured); - } - - List sharedExecutable = getApi().listTemplates("sharedexecutable", null, null, null, null, null, null, null); + if(featured != null){ + result.addAll(featured); + } + + List sharedExecutable = getApi().listTemplates("sharedexecutable", null, null, null, null, null, null, null); if(sharedExecutable != null){ result.addAll(sharedExecutable); } - + List community = getApi().listTemplates("community", null, null, null, null, null, null, null); if(community != null){ result.addAll(community); } - } - - if (result != null && result.size() > 0) { - for (CloudStackTemplate temp : result) { - EC2Image ec2Image = new EC2Image(); - ec2Image.setId(temp.getId().toString()); - ec2Image.setAccountName(temp.getAccount()); - ec2Image.setName(temp.getName()); - ec2Image.setDescription(temp.getDisplayText()); - ec2Image.setOsTypeId(temp.getOsTypeId().toString()); - ec2Image.setIsPublic(temp.getIsPublic()); - ec2Image.setIsReady(temp.getIsReady()); - ec2Image.setDomainId(temp.getDomainId()); + } + + if (result != null && result.size() > 0) { + for (CloudStackTemplate temp : result) { + EC2Image ec2Image = new EC2Image(); + ec2Image.setId(temp.getId().toString()); + ec2Image.setAccountName(temp.getAccount()); + ec2Image.setName(temp.getName()); + ec2Image.setDescription(temp.getDisplayText()); + ec2Image.setOsTypeId(temp.getOsTypeId().toString()); + ec2Image.setIsPublic(temp.getIsPublic()); + ec2Image.setIsReady(temp.getIsReady()); + ec2Image.setDomainId(temp.getDomainId()); List resourceTags = temp.getTags(); for(CloudStackKeyValue resourceTag : resourceTags) { EC2TagKeyValue param = new EC2TagKeyValue(); @@ -2014,184 +2005,184 @@ public class EC2Engine { param.setValue(resourceTag.getValue()); ec2Image.addResourceTag(param); } - images.addImage(ec2Image); - } + images.addImage(ec2Image); + } } - return images; - } catch(Exception e) { - logger.error( "List Templates - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - } + return images; + } catch(Exception e) { + logger.error( "List Templates - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } - /** - * List security groups - * - * @param interestedGroups - * @return - * @throws EC2ServiceException - * @throws UnsupportedEncodingException - * @throws SignatureException - * @throws IOException - * @throws SAXException - * @throws ParserConfigurationException - * @throws ParseException - */ - public EC2DescribeSecurityGroupsResponse listSecurityGroups( String[] interestedGroups ) throws Exception { - try { - EC2DescribeSecurityGroupsResponse groupSet = new EC2DescribeSecurityGroupsResponse(); + /** + * List security groups + * + * @param interestedGroups + * @return + * @throws EC2ServiceException + * @throws UnsupportedEncodingException + * @throws SignatureException + * @throws IOException + * @throws SAXException + * @throws ParserConfigurationException + * @throws ParseException + */ + public EC2DescribeSecurityGroupsResponse listSecurityGroups( String[] interestedGroups ) throws Exception { + try { + EC2DescribeSecurityGroupsResponse groupSet = new EC2DescribeSecurityGroupsResponse(); List groups = getApi().listSecurityGroups(null, null, null, true, null, null, null); - if (groups != null && groups.size() > 0) - for (CloudStackSecurityGroup group : groups) { - boolean matched = false; - if (interestedGroups.length > 0) { - for (String groupName :interestedGroups) { - if (groupName.equalsIgnoreCase(group.getName())) { - matched = true; - break; - } - } - } else { - matched = true; - } - if (!matched) continue; - EC2SecurityGroup ec2Group = new EC2SecurityGroup(); - // not sure if we should set both account and account name to accountname - ec2Group.setAccount(group.getAccountName()); - ec2Group.setAccountName(group.getAccountName()); - ec2Group.setName(group.getName()); - ec2Group.setDescription(group.getDescription()); - ec2Group.setDomainId(group.getDomainId()); - ec2Group.setId(group.getId().toString()); - toPermission(ec2Group, group); - - groupSet.addGroup(ec2Group); - } - return groupSet; - } catch(Exception e) { - logger.error( "List Security Groups - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); - } - } + if (groups != null && groups.size() > 0) + for (CloudStackSecurityGroup group : groups) { + boolean matched = false; + if (interestedGroups.length > 0) { + for (String groupName :interestedGroups) { + if (groupName.equalsIgnoreCase(group.getName())) { + matched = true; + break; + } + } + } else { + matched = true; + } + if (!matched) continue; + EC2SecurityGroup ec2Group = new EC2SecurityGroup(); + // not sure if we should set both account and account name to accountname + ec2Group.setAccount(group.getAccountName()); + ec2Group.setAccountName(group.getAccountName()); + ec2Group.setName(group.getName()); + ec2Group.setDescription(group.getDescription()); + ec2Group.setDomainId(group.getDomainId()); + ec2Group.setId(group.getId().toString()); + toPermission(ec2Group, group); - /** - * Convert ingress rule to EC2IpPermission records - * - * @param response - * @param group - * @return - */ - private boolean toPermission(EC2SecurityGroup response, CloudStackSecurityGroup group ) { - List rules = group.getIngressRules(); + groupSet.addGroup(ec2Group); + } + return groupSet; + } catch(Exception e) { + logger.error( "List Security Groups - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } - if (rules == null || rules.isEmpty()) return false; + /** + * Convert ingress rule to EC2IpPermission records + * + * @param response + * @param group + * @return + */ + private boolean toPermission(EC2SecurityGroup response, CloudStackSecurityGroup group ) { + List rules = group.getIngressRules(); - for (CloudStackIngressRule rule : rules) { - EC2IpPermission perm = new EC2IpPermission(); - perm.setProtocol(rule.getProtocol()); - perm.setFromPort(rule.getStartPort()); - perm.setToPort(rule.getEndPort()); - perm.setRuleId(rule.getRuleId() != null ? rule.getRuleId().toString() : new String()); - perm.setIcmpCode(rule.getIcmpCode() != null ? rule.getIcmpCode().toString() : new String()); - perm.setIcmpType(rule.getIcmpType() != null ? rule.getIcmpType().toString() : new String()); - perm.setCIDR(rule.getCidr()); - perm.addIpRange(rule.getCidr()); + if (rules == null || rules.isEmpty()) return false; - if (rule.getAccountName() != null && rule.getSecurityGroupName() != null) { - EC2SecurityGroup newGroup = new EC2SecurityGroup(); - newGroup.setAccount(rule.getAccountName()); - newGroup.setName(rule.getSecurityGroupName()); - perm.addUser(newGroup); - } - response.addIpPermission(perm); - } - return true; - } + for (CloudStackIngressRule rule : rules) { + EC2IpPermission perm = new EC2IpPermission(); + perm.setProtocol(rule.getProtocol()); + perm.setFromPort(rule.getStartPort()); + perm.setToPort(rule.getEndPort()); + perm.setRuleId(rule.getRuleId() != null ? rule.getRuleId().toString() : new String()); + perm.setIcmpCode(rule.getIcmpCode() != null ? rule.getIcmpCode().toString() : new String()); + perm.setIcmpType(rule.getIcmpType() != null ? rule.getIcmpType().toString() : new String()); + perm.setCIDR(rule.getCidr()); + perm.addIpRange(rule.getCidr()); - /** - * Find the current account based on the SecretKey - * - * @return - * @throws Exception - */ - public CloudStackAccount getCurrentAccount() throws Exception { - if (currentAccount != null) { - // verify this is the same account!!! - for (CloudStackUser user : currentAccount.getUser()) { - if (user.getSecretkey() != null && user.getSecretkey().equalsIgnoreCase(UserContext.current().getSecretKey())) { - return currentAccount; - } - } - } - // otherwise let's find this user/account - List accounts = getApi().listAccounts(null, null, null, null, null, null, null, null); - for (CloudStackAccount account : accounts) { - CloudStackUser[] users = account.getUser(); - for (CloudStackUser user : users) { - String userSecretKey = user.getSecretkey(); - if (userSecretKey != null && userSecretKey.equalsIgnoreCase(UserContext.current().getSecretKey())) { - currentAccount = account; - return account; - } - } - } - // if we get here, there is something wrong... - return null; - } + if (rule.getAccountName() != null && rule.getSecurityGroupName() != null) { + EC2SecurityGroup newGroup = new EC2SecurityGroup(); + newGroup.setAccount(rule.getAccountName()); + newGroup.setName(rule.getSecurityGroupName()); + perm.addUser(newGroup); + } + response.addIpPermission(perm); + } + return true; + } - /** - * List networkOfferings by zone with securityGroup enabled - * - * @param zoneId - * @return - * @throws Exception - */ - private CloudStackNetwork getNetworksWithSecurityGroupEnabled(String zoneId) throws Exception { - List networks = getApi().listNetworks(null, null, null, null, null, null, null, null, null, zoneId); - List netWithSecGroup = new ArrayList(); - for (CloudStackNetwork network : networks ) { - if (!network.getNetworkOfferingAvailability().equalsIgnoreCase("unavailable") && network.getSecurityGroupEnabled()) - netWithSecGroup.add(network); - } - // we'll take the first one - return netWithSecGroup.get(0); - } + /** + * Find the current account based on the SecretKey + * + * @return + * @throws Exception + */ + public CloudStackAccount getCurrentAccount() throws Exception { + if (currentAccount != null) { + // verify this is the same account!!! + for (CloudStackUser user : currentAccount.getUser()) { + if (user.getSecretkey() != null && user.getSecretkey().equalsIgnoreCase(UserContext.current().getSecretKey())) { + return currentAccount; + } + } + } + // otherwise let's find this user/account + List accounts = getApi().listAccounts(null, null, null, null, null, null, null, null); + for (CloudStackAccount account : accounts) { + CloudStackUser[] users = account.getUser(); + for (CloudStackUser user : users) { + String userSecretKey = user.getSecretkey(); + if (userSecretKey != null && userSecretKey.equalsIgnoreCase(UserContext.current().getSecretKey())) { + currentAccount = account; + return account; + } + } + } + // if we get here, there is something wrong... + return null; + } - /** - * Create a network - * - * @param zoneId - * @param offering - * @param owner - * @return - * @throws Exception - */ - private CloudStackNetwork createDefaultGuestNetwork(String zoneId, CloudStackNetworkOffering offering, CloudStackAccount owner) throws Exception { - return getApi().createNetwork(owner.getName() + "-network", owner.getName() + "-network", offering.getId(), zoneId, owner.getName(), - owner.getDomainId(), true, null, null, null, null, null, null, null, null); - } + /** + * List networkOfferings by zone with securityGroup enabled + * + * @param zoneId + * @return + * @throws Exception + */ + private CloudStackNetwork getNetworksWithSecurityGroupEnabled(String zoneId) throws Exception { + List networks = getApi().listNetworks(null, null, null, null, null, null, null, null, null, zoneId); + List netWithSecGroup = new ArrayList(); + for (CloudStackNetwork network : networks ) { + if (!network.getNetworkOfferingAvailability().equalsIgnoreCase("unavailable") && network.getSecurityGroupEnabled()) + netWithSecGroup.add(network); + } + // we'll take the first one + return netWithSecGroup.get(0); + } - /** - * List of networks without securityGroup enabled by zone - * - * @param zoneId - * @return - * @throws Exception - */ - private CloudStackNetwork getNetworksWithoutSecurityGroupEnabled(String zoneId) throws Exception { - // grab current account - CloudStackAccount caller = getCurrentAccount(); - - //check if account has any networks in the system - List networks = getApi().listNetworks(caller.getName(), caller.getDomainId(), null, true, null, null, null, null, null, zoneId); - - //listRequired offerings in the system - the network created from this offering has to be specified in deployVm command - List reuquiredOfferings = getApi().listNetworkOfferings("Required", null, null, null, true, null, null, null, null, null, zoneId); - if (reuquiredOfferings != null && !reuquiredOfferings.isEmpty()) { - if (networks != null && !networks.isEmpty()) { - //pick up the first required network from the network list - for (CloudStackNetwork network : networks) { + /** + * Create a network + * + * @param zoneId + * @param offering + * @param owner + * @return + * @throws Exception + */ + private CloudStackNetwork createDefaultGuestNetwork(String zoneId, CloudStackNetworkOffering offering, CloudStackAccount owner) throws Exception { + return getApi().createNetwork(owner.getName() + "-network", owner.getName() + "-network", offering.getId(), zoneId, owner.getName(), + owner.getDomainId(), true, null, null, null, null, null, null, null, null); + } + + /** + * List of networks without securityGroup enabled by zone + * + * @param zoneId + * @return + * @throws Exception + */ + private CloudStackNetwork getNetworksWithoutSecurityGroupEnabled(String zoneId) throws Exception { + // grab current account + CloudStackAccount caller = getCurrentAccount(); + + //check if account has any networks in the system + List networks = getApi().listNetworks(caller.getName(), caller.getDomainId(), null, true, null, null, null, null, null, zoneId); + + //listRequired offerings in the system - the network created from this offering has to be specified in deployVm command + List reuquiredOfferings = getApi().listNetworkOfferings("Required", null, null, null, true, null, null, null, null, null, zoneId); + if (reuquiredOfferings != null && !reuquiredOfferings.isEmpty()) { + if (networks != null && !networks.isEmpty()) { + //pick up the first required network from the network list + for (CloudStackNetwork network : networks) { for (CloudStackNetworkOffering requiredOffering : reuquiredOfferings) { logger.debug("[reqd/virtual} offering: " + requiredOffering.getId() + " network " + network.getNetworkOfferingId()); if (network.getNetworkOfferingId().equals(requiredOffering.getId())) { @@ -2199,178 +2190,178 @@ public class EC2Engine { } } } - } else { - //create new network and return it - return createDefaultGuestNetwork(zoneId, reuquiredOfferings.get(0), caller); - } - } else { - //find all optional network offerings in the system - List optionalOfferings = getApi().listNetworkOfferings("Optional", null, null, null, true, null, null, null, null, null, zoneId); - if (optionalOfferings != null && !optionalOfferings.isEmpty()) { - if (networks != null && !networks.isEmpty()) { - for (CloudStackNetwork network : networks) { - for (CloudStackNetworkOffering optionalOffering : optionalOfferings) { - logger.debug("[optional] offering: " + optionalOffering.getId() + " network " + network.getNetworkOfferingId()); - if (network.getNetworkOfferingId().equals(optionalOffering.getId())) { - return network; - } - } - } - } - } - } - - // if we get this far and haven't returned already return an error - throw new EC2ServiceException(ServerError.InternalError, "Unable to find an appropriate network for account " + caller.getName()); - } - - /** - * Find a suitable network to use for deployVM - * - * @param zone - * @return - * @throws Exception - */ - private CloudStackNetwork findNetwork(CloudStackZone zone) throws Exception { - if (zone == null) return null; - - // for basic networking, we don't specify a networkid for deployvm - if (zone.getNetworkType().equalsIgnoreCase("basic")) return null; - - if (zone.getSecurityGroupsEnabled()) { - // find system security group enabled network - return getNetworksWithSecurityGroupEnabled(zone.getId()); - - } else { - return getNetworksWithoutSecurityGroupEnabled(zone.getId()); - } - } - - private CloudStackZone findZone() throws Exception { - CloudStackAccount caller = getCurrentAccount(); - List cloudZones; - - String defaultZoneId = getDefaultZoneId(caller.getId()); - if (defaultZoneId != null) { - cloudZones = getApi().listZones(true, null, defaultZoneId, null); } else { + //create new network and return it + return createDefaultGuestNetwork(zoneId, reuquiredOfferings.get(0), caller); + } + } else { + //find all optional network offerings in the system + List optionalOfferings = getApi().listNetworkOfferings("Optional", null, null, null, true, null, null, null, null, null, zoneId); + if (optionalOfferings != null && !optionalOfferings.isEmpty()) { + if (networks != null && !networks.isEmpty()) { + for (CloudStackNetwork network : networks) { + for (CloudStackNetworkOffering optionalOffering : optionalOfferings) { + logger.debug("[optional] offering: " + optionalOffering.getId() + " network " + network.getNetworkOfferingId()); + if (network.getNetworkOfferingId().equals(optionalOffering.getId())) { + return network; + } + } + } + } + } + } + + // if we get this far and haven't returned already return an error + throw new EC2ServiceException(ServerError.InternalError, "Unable to find an appropriate network for account " + caller.getName()); + } + + /** + * Find a suitable network to use for deployVM + * + * @param zone + * @return + * @throws Exception + */ + private CloudStackNetwork findNetwork(CloudStackZone zone) throws Exception { + if (zone == null) return null; + + // for basic networking, we don't specify a networkid for deployvm + if (zone.getNetworkType().equalsIgnoreCase("basic")) return null; + + if (zone.getSecurityGroupsEnabled()) { + // find system security group enabled network + return getNetworksWithSecurityGroupEnabled(zone.getId()); + + } else { + return getNetworksWithoutSecurityGroupEnabled(zone.getId()); + } + } + + private CloudStackZone findZone() throws Exception { + CloudStackAccount caller = getCurrentAccount(); + List cloudZones; + + String defaultZoneId = getDefaultZoneId(caller.getId()); + if (defaultZoneId != null) { + cloudZones = getApi().listZones(true, null, defaultZoneId, null); + } else { // caller.getDomainId doesn't work in user mode // List cloudZones = getApi().listZones(true, caller.getDomainId(), null, null); - cloudZones = getApi().listZones(true, null, null, null); - } - if (cloudZones != null && cloudZones.size() > 0) { - return cloudZones.get(0); - } - return null; + cloudZones = getApi().listZones(true, null, null, null); } + if (cloudZones != null && cloudZones.size() > 0) { + return cloudZones.get(0); + } + return null; + } - /** - * Finds the defaultZone marked for the account - */ - private String getDefaultZoneId(String accountId) { - try { - return accDao.getDefaultZoneId(accountId); - } catch(Exception e) { - logger.error( "Error while retrieving Account information by id - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + /** + * Finds the defaultZone marked for the account + */ + private String getDefaultZoneId(String accountId) { + try { + return accDao.getDefaultZoneId(accountId); + } catch(Exception e) { + logger.error( "Error while retrieving Account information by id - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } + + /** + * Windows has its own device strings. + * + * @param hypervisor + * @param deviceId + * @return + */ + public String cloudDeviceIdToDevicePath( String hypervisor, String deviceId ) + { + Integer devId = new Integer(deviceId); + if (null != hypervisor && hypervisor.toLowerCase().contains( "windows" )) { + switch( devId ) { + case 1: return "xvdb"; + case 2: return "xvdc"; + case 3: return "xvdd"; + case 4: return "xvde"; + case 5: return "xvdf"; + case 6: return "xvdg"; + case 7: return "xvdh"; + case 8: return "xvdi"; + case 9: return "xvdj"; + default: return new String( "" + deviceId ); + } + } else { // -> assume its unix + switch( devId ) { + case 1: return "/dev/sdb"; + case 2: return "/dev/sdc"; + case 3: return "/dev/sdd"; + case 4: return "/dev/sde"; + case 5: return "/dev/sdf"; + case 6: return "/dev/sdg"; + case 7: return "/dev/sdh"; + case 8: return "/dev/sdi"; + case 9: return "/dev/sdj"; + default: return new String( "" + deviceId ); } } - - /** - * Windows has its own device strings. - * - * @param hypervisor - * @param deviceId - * @return - */ - public String cloudDeviceIdToDevicePath( String hypervisor, String deviceId ) - { - Integer devId = new Integer(deviceId); - if (null != hypervisor && hypervisor.toLowerCase().contains( "windows" )) { - switch( devId ) { - case 1: return "xvdb"; - case 2: return "xvdc"; - case 3: return "xvdd"; - case 4: return "xvde"; - case 5: return "xvdf"; - case 6: return "xvdg"; - case 7: return "xvdh"; - case 8: return "xvdi"; - case 9: return "xvdj"; - default: return new String( "" + deviceId ); - } - } else { // -> assume its unix - switch( devId ) { - case 1: return "/dev/sdb"; - case 2: return "/dev/sdc"; - case 3: return "/dev/sdd"; - case 4: return "/dev/sde"; - case 5: return "/dev/sdf"; - case 6: return "/dev/sdg"; - case 7: return "/dev/sdh"; - case 8: return "/dev/sdi"; - case 9: return "/dev/sdj"; - default: return new String( "" + deviceId ); - } - } - } + } - /** - * Translate the device name string into a Cloud Stack deviceId. - * deviceId 3 is reserved for CDROM and 0 for the ROOT disk - * - * @param device string - * @return deviceId value - */ - private String mapDeviceToCloudDeviceId( String device ) - { - if (device.equalsIgnoreCase( "/dev/sdb" )) return "1"; - else if (device.equalsIgnoreCase( "/dev/sdc" )) return "2"; - else if (device.equalsIgnoreCase( "/dev/sde" )) return "4"; - else if (device.equalsIgnoreCase( "/dev/sdf" )) return "5"; - else if (device.equalsIgnoreCase( "/dev/sdg" )) return "6"; - else if (device.equalsIgnoreCase( "/dev/sdh" )) return "7"; - else if (device.equalsIgnoreCase( "/dev/sdi" )) return "8"; - else if (device.equalsIgnoreCase( "/dev/sdj" )) return "9"; + /** + * Translate the device name string into a Cloud Stack deviceId. + * deviceId 3 is reserved for CDROM and 0 for the ROOT disk + * + * @param device string + * @return deviceId value + */ + private String mapDeviceToCloudDeviceId( String device ) + { + if (device.equalsIgnoreCase( "/dev/sdb" )) return "1"; + else if (device.equalsIgnoreCase( "/dev/sdc" )) return "2"; + else if (device.equalsIgnoreCase( "/dev/sde" )) return "4"; + else if (device.equalsIgnoreCase( "/dev/sdf" )) return "5"; + else if (device.equalsIgnoreCase( "/dev/sdg" )) return "6"; + else if (device.equalsIgnoreCase( "/dev/sdh" )) return "7"; + else if (device.equalsIgnoreCase( "/dev/sdi" )) return "8"; + else if (device.equalsIgnoreCase( "/dev/sdj" )) return "9"; - else if (device.equalsIgnoreCase( "/dev/xvdb" )) return "1"; - else if (device.equalsIgnoreCase( "/dev/xvdc" )) return "2"; - else if (device.equalsIgnoreCase( "/dev/xvde" )) return "4"; - else if (device.equalsIgnoreCase( "/dev/xvdf" )) return "5"; - else if (device.equalsIgnoreCase( "/dev/xvdg" )) return "6"; - else if (device.equalsIgnoreCase( "/dev/xvdh" )) return "7"; - else if (device.equalsIgnoreCase( "/dev/xvdi" )) return "8"; - else if (device.equalsIgnoreCase( "/dev/xvdj" )) return "9"; + else if (device.equalsIgnoreCase( "/dev/xvdb" )) return "1"; + else if (device.equalsIgnoreCase( "/dev/xvdc" )) return "2"; + else if (device.equalsIgnoreCase( "/dev/xvde" )) return "4"; + else if (device.equalsIgnoreCase( "/dev/xvdf" )) return "5"; + else if (device.equalsIgnoreCase( "/dev/xvdg" )) return "6"; + else if (device.equalsIgnoreCase( "/dev/xvdh" )) return "7"; + else if (device.equalsIgnoreCase( "/dev/xvdi" )) return "8"; + else if (device.equalsIgnoreCase( "/dev/xvdj" )) return "9"; - else if (device.equalsIgnoreCase( "xvdb" )) return "1"; - else if (device.equalsIgnoreCase( "xvdc" )) return "2"; - else if (device.equalsIgnoreCase( "xvde" )) return "4"; - else if (device.equalsIgnoreCase( "xvdf" )) return "5"; - else if (device.equalsIgnoreCase( "xvdg" )) return "6"; - else if (device.equalsIgnoreCase( "xvdh" )) return "7"; - else if (device.equalsIgnoreCase( "xvdi" )) return "8"; - else if (device.equalsIgnoreCase( "xvdj" )) return "9"; + else if (device.equalsIgnoreCase( "xvdb" )) return "1"; + else if (device.equalsIgnoreCase( "xvdc" )) return "2"; + else if (device.equalsIgnoreCase( "xvde" )) return "4"; + else if (device.equalsIgnoreCase( "xvdf" )) return "5"; + else if (device.equalsIgnoreCase( "xvdg" )) return "6"; + else if (device.equalsIgnoreCase( "xvdh" )) return "7"; + else if (device.equalsIgnoreCase( "xvdi" )) return "8"; + else if (device.equalsIgnoreCase( "xvdj" )) return "9"; - else throw new EC2ServiceException( ClientError.Unsupported, device + " is not supported" ); - } + else throw new EC2ServiceException( ClientError.Unsupported, device + " is not supported" ); + } - /** - * Map CloudStack instance state to Amazon state strings - * - * @param state - * @return - */ - private String mapToAmazonVolState( String state ) - { - if (state.equalsIgnoreCase( "Allocated" ) || - state.equalsIgnoreCase( "Creating" ) || - state.equalsIgnoreCase( "Ready" )) return "available"; + /** + * Map CloudStack instance state to Amazon state strings + * + * @param state + * @return + */ + private String mapToAmazonVolState( String state ) + { + if (state.equalsIgnoreCase( "Allocated" ) || + state.equalsIgnoreCase( "Creating" ) || + state.equalsIgnoreCase( "Ready" )) return "available"; - if (state.equalsIgnoreCase( "Destroy" )) return "deleting"; + if (state.equalsIgnoreCase( "Destroy" )) return "deleting"; - return "error"; - } + return "error"; + } /** * Map Amazon resourceType to CloudStack resourceType @@ -2402,45 +2393,45 @@ public class EC2Engine { return (resourceType.toLowerCase()); } - /** - * Stop an instance - * Wait until one specific VM has stopped - * - * @param instanceId - * @return - * @throws Exception - */ - private boolean stopVirtualMachine( String instanceId) throws Exception { - try { - CloudStackUserVm resp = getApi().stopVirtualMachine(instanceId, false); - if (logger.isDebugEnabled()) - logger.debug("Stopping VM " + instanceId ); - return resp != null; - } catch(Exception e) { - logger.error( "StopVirtualMachine - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } + /** + * Stop an instance + * Wait until one specific VM has stopped + * + * @param instanceId + * @return + * @throws Exception + */ + private boolean stopVirtualMachine( String instanceId) throws Exception { + try { + CloudStackUserVm resp = getApi().stopVirtualMachine(instanceId, false); + if (logger.isDebugEnabled()) + logger.debug("Stopping VM " + instanceId ); + return resp != null; + } catch(Exception e) { + logger.error( "StopVirtualMachine - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } + + /** + * Start an existing stopped instance(VM) + * + * @param instanceId + * @return + * @throws Exception + */ + private boolean startVirtualMachine( String instanceId ) throws Exception { + try { + CloudStackUserVm resp = getApi().startVirtualMachine(instanceId); + if (logger.isDebugEnabled()) + logger.debug("Starting VM " + instanceId ); + return resp != null; + } catch(Exception e) { + logger.error("StartVirtualMachine - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); + } + } - /** - * Start an existing stopped instance(VM) - * - * @param instanceId - * @return - * @throws Exception - */ - private boolean startVirtualMachine( String instanceId ) throws Exception { - try { - CloudStackUserVm resp = getApi().startVirtualMachine(instanceId); - if (logger.isDebugEnabled()) - logger.debug("Starting VM " + instanceId ); - return resp != null; - } catch(Exception e) { - logger.error("StartVirtualMachine - ", e); - throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred."); - } - } - /** * Cloud Stack API takes a comma separated list as a parameter. * diff --git a/awsapi/src/com/cloud/bridge/service/core/s3/S3Engine.java b/awsapi/src/com/cloud/bridge/service/core/s3/S3Engine.java index 916c51d846c..2ce9e339255 100644 --- a/awsapi/src/com/cloud/bridge/service/core/s3/S3Engine.java +++ b/awsapi/src/com/cloud/bridge/service/core/s3/S3Engine.java @@ -33,13 +33,14 @@ import java.util.Set; import java.util.TimeZone; import java.util.UUID; +import javax.inject.Inject; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import org.json.simple.parser.ParseException; -import com.cloud.bridge.io.S3FileSystemBucketAdapter; import com.cloud.bridge.io.S3CAStorBucketAdapter; +import com.cloud.bridge.io.S3FileSystemBucketAdapter; import com.cloud.bridge.model.BucketPolicyVO; import com.cloud.bridge.model.MHostMountVO; import com.cloud.bridge.model.MHostVO; @@ -50,27 +51,18 @@ import com.cloud.bridge.model.SBucketVO; import com.cloud.bridge.model.SHost; import com.cloud.bridge.model.SHostVO; import com.cloud.bridge.model.SMetaVO; -import com.cloud.bridge.model.SObjectVO; import com.cloud.bridge.model.SObjectItemVO; +import com.cloud.bridge.model.SObjectVO; import com.cloud.bridge.persist.dao.BucketPolicyDao; -import com.cloud.bridge.persist.dao.BucketPolicyDaoImpl; import com.cloud.bridge.persist.dao.MHostDao; -import com.cloud.bridge.persist.dao.MHostDaoImpl; import com.cloud.bridge.persist.dao.MHostMountDao; -import com.cloud.bridge.persist.dao.MHostMountDaoImpl; import com.cloud.bridge.persist.dao.MultipartLoadDao; import com.cloud.bridge.persist.dao.SAclDao; -import com.cloud.bridge.persist.dao.SAclDaoImpl; import com.cloud.bridge.persist.dao.SBucketDao; -import com.cloud.bridge.persist.dao.SBucketDaoImpl; import com.cloud.bridge.persist.dao.SHostDao; -import com.cloud.bridge.persist.dao.SHostDaoImpl; import com.cloud.bridge.persist.dao.SMetaDao; -import com.cloud.bridge.persist.dao.SMetaDaoImpl; import com.cloud.bridge.persist.dao.SObjectDao; -import com.cloud.bridge.persist.dao.SObjectDaoImpl; import com.cloud.bridge.persist.dao.SObjectItemDao; -import com.cloud.bridge.persist.dao.SObjectItemDaoImpl; import com.cloud.bridge.service.UserContext; import com.cloud.bridge.service.controller.s3.ServiceProvider; import com.cloud.bridge.service.core.s3.S3BucketPolicy.PolicyAccess; @@ -86,11 +78,10 @@ import com.cloud.bridge.service.exception.OutOfServiceException; import com.cloud.bridge.service.exception.OutOfStorageException; import com.cloud.bridge.service.exception.PermissionDeniedException; import com.cloud.bridge.util.DateHelper; +import com.cloud.bridge.util.OrderedPair; import com.cloud.bridge.util.PolicyParser; import com.cloud.bridge.util.StringHelper; -import com.cloud.bridge.util.OrderedPair; import com.cloud.bridge.util.Triple; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; @@ -99,105 +90,105 @@ import com.cloud.utils.db.Transaction; */ public class S3Engine { protected final static Logger logger = Logger.getLogger(S3Engine.class); - protected final SHostDao shostDao = ComponentLocator.inject(SHostDaoImpl.class); - protected final MHostDao mhostDao = ComponentLocator.inject(MHostDaoImpl.class); - protected final static BucketPolicyDao bPolicy = ComponentLocator.inject(BucketPolicyDaoImpl.class); - protected final BucketPolicyDao bPolicyDao = ComponentLocator.inject(BucketPolicyDaoImpl.class); - protected final SBucketDao bucketDao = ComponentLocator.inject(SBucketDaoImpl.class); - protected final SAclDao aclDao = ComponentLocator.inject(SAclDaoImpl.class); - protected final static SAclDao saclDao = ComponentLocator.inject(SAclDaoImpl.class); - protected final SObjectDao objectDao = ComponentLocator.inject(SObjectDaoImpl.class); - protected final SObjectItemDao itemDao = ComponentLocator.inject(SObjectItemDaoImpl.class); - protected final SMetaDao metaDao = ComponentLocator.inject(SMetaDaoImpl.class); - protected final MHostMountDao mountDao = ComponentLocator.inject(MHostMountDaoImpl.class); + @Inject SHostDao shostDao; + @Inject MHostDao mhostDao; + @Inject static BucketPolicyDao bPolicy; + @Inject BucketPolicyDao bPolicyDao; + @Inject SBucketDao bucketDao; + @Inject SAclDao aclDao; + @Inject static SAclDao saclDao; + @Inject SObjectDao objectDao; + @Inject SObjectItemDao itemDao; + @Inject SMetaDao metaDao; + @Inject MHostMountDao mountDao; private final int LOCK_ACQUIRING_TIMEOUT_SECONDS = 10; // ten seconds private final Map bucketAdapters = new HashMap(); - + public S3Engine() { - bucketAdapters.put(SHost.STORAGE_HOST_TYPE_LOCAL, new S3FileSystemBucketAdapter()); + bucketAdapters.put(SHost.STORAGE_HOST_TYPE_LOCAL, new S3FileSystemBucketAdapter()); bucketAdapters.put(SHost.STORAGE_HOST_TYPE_CASTOR, new S3CAStorBucketAdapter()); } - - + + /** * Return a S3CopyObjectResponse which represents an object being copied from source * to destination bucket. * Called from S3ObjectAction when copying an object. * This can be treated as first a GET followed by a PUT of the object the user wants to copy. */ - - public S3CopyObjectResponse handleRequest(S3CopyObjectRequest request) - { - S3CopyObjectResponse response = new S3CopyObjectResponse(); - - // [A] Get the object we want to copy - S3GetObjectRequest getRequest = new S3GetObjectRequest(); - getRequest.setBucketName(request.getSourceBucketName()); - getRequest.setKey(request.getSourceKey()); - getRequest.setVersion(request.getVersion()); - getRequest.setConditions( request.getConditions()); - getRequest.setInlineData( true ); - getRequest.setReturnData( true ); - if ( MetadataDirective.COPY == request.getDirective()) - getRequest.setReturnMetadata( true ); - else getRequest.setReturnMetadata( false ); - - //-> before we do anything verify the permissions on a copy basis - String destinationBucketName = request.getDestinationBucketName(); - String destinationKeyName = request.getDestinationKey(); - S3PolicyContext context = new S3PolicyContext( PolicyActions.PutObject, destinationBucketName ); - context.setKeyName( destinationKeyName ); - context.setEvalParam( ConditionKeys.MetaData, request.getDirective().toString()); - context.setEvalParam( ConditionKeys.CopySource, "/" + request.getSourceBucketName() + "/" + request.getSourceKey()); - if (PolicyAccess.DENY == verifyPolicy( context )) + public S3CopyObjectResponse handleRequest(S3CopyObjectRequest request) + { + S3CopyObjectResponse response = new S3CopyObjectResponse(); + + // [A] Get the object we want to copy + S3GetObjectRequest getRequest = new S3GetObjectRequest(); + getRequest.setBucketName(request.getSourceBucketName()); + getRequest.setKey(request.getSourceKey()); + getRequest.setVersion(request.getVersion()); + getRequest.setConditions( request.getConditions()); + + getRequest.setInlineData( true ); + getRequest.setReturnData( true ); + if ( MetadataDirective.COPY == request.getDirective()) + getRequest.setReturnMetadata( true ); + else getRequest.setReturnMetadata( false ); + + //-> before we do anything verify the permissions on a copy basis + String destinationBucketName = request.getDestinationBucketName(); + String destinationKeyName = request.getDestinationKey(); + S3PolicyContext context = new S3PolicyContext( PolicyActions.PutObject, destinationBucketName ); + context.setKeyName( destinationKeyName ); + context.setEvalParam( ConditionKeys.MetaData, request.getDirective().toString()); + context.setEvalParam( ConditionKeys.CopySource, "/" + request.getSourceBucketName() + "/" + request.getSourceKey()); + if (PolicyAccess.DENY == verifyPolicy( context )) throw new PermissionDeniedException( "Access Denied - bucket policy DENY result" ); - - S3GetObjectResponse originalObject = handleRequest(getRequest); - int resultCode = originalObject.getResultCode(); - if (200 != resultCode) { - response.setResultCode( resultCode ); - response.setResultDescription( originalObject.getResultDescription()); - return response; - } - - response.setCopyVersion( originalObject.getVersion()); - - // [B] Put the object into the destination bucket - S3PutObjectInlineRequest putRequest = new S3PutObjectInlineRequest(); - putRequest.setBucketName(request.getDestinationBucketName()) ; - putRequest.setKey(destinationKeyName); - if ( MetadataDirective.COPY == request.getDirective()) - putRequest.setMetaEntries(originalObject.getMetaEntries()); - else putRequest.setMetaEntries(request.getMetaEntries()); - putRequest.setAcl(request.getAcl()); // -> if via a SOAP call - putRequest.setCannedAccess(request.getCannedAccess()); // -> if via a REST call - putRequest.setContentLength(originalObject.getContentLength()); - putRequest.setData(originalObject.getData()); + S3GetObjectResponse originalObject = handleRequest(getRequest); + int resultCode = originalObject.getResultCode(); + if (200 != resultCode) { + response.setResultCode( resultCode ); + response.setResultDescription( originalObject.getResultDescription()); + return response; + } - S3PutObjectInlineResponse putResp = handleRequest(putRequest); - response.setResultCode( putResp.resultCode ); - response.setResultDescription( putResp.getResultDescription()); - response.setETag( putResp.getETag()); - response.setLastModified( putResp.getLastModified()); - response.setPutVersion( putResp.getVersion()); - return response; - } + response.setCopyVersion( originalObject.getVersion()); + + + // [B] Put the object into the destination bucket + S3PutObjectInlineRequest putRequest = new S3PutObjectInlineRequest(); + putRequest.setBucketName(request.getDestinationBucketName()) ; + putRequest.setKey(destinationKeyName); + if ( MetadataDirective.COPY == request.getDirective()) + putRequest.setMetaEntries(originalObject.getMetaEntries()); + else putRequest.setMetaEntries(request.getMetaEntries()); + putRequest.setAcl(request.getAcl()); // -> if via a SOAP call + putRequest.setCannedAccess(request.getCannedAccess()); // -> if via a REST call + putRequest.setContentLength(originalObject.getContentLength()); + putRequest.setData(originalObject.getData()); + + S3PutObjectInlineResponse putResp = handleRequest(putRequest); + response.setResultCode( putResp.resultCode ); + response.setResultDescription( putResp.getResultDescription()); + response.setETag( putResp.getETag()); + response.setLastModified( putResp.getLastModified()); + response.setPutVersion( putResp.getVersion()); + return response; + } public S3CreateBucketResponse handleRequest(S3CreateBucketRequest request) { - S3CreateBucketResponse response = new S3CreateBucketResponse(); - String cannedAccessPolicy = request.getCannedAccess(); - String bucketName = request.getBucketName(); - response.setBucketName( bucketName ); - Transaction txn= null; - verifyBucketName( bucketName, false ); - - S3PolicyContext context = new S3PolicyContext( PolicyActions.CreateBucket, bucketName ); - context.setEvalParam( ConditionKeys.Acl, cannedAccessPolicy ); - if (PolicyAccess.DENY == verifyPolicy( context )) + S3CreateBucketResponse response = new S3CreateBucketResponse(); + String cannedAccessPolicy = request.getCannedAccess(); + String bucketName = request.getBucketName(); + response.setBucketName( bucketName ); + Transaction txn= null; + verifyBucketName( bucketName, false ); + + S3PolicyContext context = new S3PolicyContext( PolicyActions.CreateBucket, bucketName ); + context.setEvalParam( ConditionKeys.Acl, cannedAccessPolicy ); + if (PolicyAccess.DENY == verifyPolicy( context )) throw new PermissionDeniedException( "Access Denied - bucket policy DENY result" ); OrderedPair shost_storagelocation_pair = null; boolean success = false; @@ -211,7 +202,7 @@ public class S3Engine { request.getBucketName(), null); SBucketVO sbucket = new SBucketVO(request.getBucketName(), DateHelper.currentGMTTime(), UserContext.current() - .getCanonicalUserId(), + .getCanonicalUserId(), shost_storagelocation_pair.getFirst()); shost_storagelocation_pair.getFirst().getBuckets().add(sbucket); @@ -239,29 +230,29 @@ public class S3Engine { txn.rollback(); txn.close(); } - return response; + return response; } - + /** * Return a S3Response which represents the effect of an object being deleted from its bucket. * Called from S3BucketAction when deleting an object. */ - + public S3Response handleRequest( S3DeleteBucketRequest request ) { - S3Response response = new S3Response(); - // - String bucketName = request.getBucketName(); - SBucketVO sbucket = bucketDao.getByName(bucketName); - - Transaction txn = null; - if ( sbucket != null ) - { - txn = Transaction.open(Transaction.AWSAPI_DB); - txn.start(); - S3PolicyContext context = new S3PolicyContext( PolicyActions.DeleteBucket, bucketName ); - switch( verifyPolicy( context )) - { + S3Response response = new S3Response(); + // + String bucketName = request.getBucketName(); + SBucketVO sbucket = bucketDao.getByName(bucketName); + + Transaction txn = null; + if ( sbucket != null ) + { + txn = Transaction.open(Transaction.AWSAPI_DB); + txn.start(); + S3PolicyContext context = new S3PolicyContext( PolicyActions.DeleteBucket, bucketName ); + switch( verifyPolicy( context )) + { case ALLOW: // The bucket policy can give users permission to delete a // bucket whereas ACLs cannot @@ -282,110 +273,110 @@ public class S3Engine { } break; } - - // Delete the file from its storage location - OrderedPair host_storagelocation_pair = getBucketStorageHost(sbucket); - S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(host_storagelocation_pair.getFirst()); - bucketAdapter.deleteContainer(host_storagelocation_pair.getSecond(), request.getBucketName()); - - // Cascade-deleting can delete related SObject/SObjectItem objects, but not SAcl, SMeta and policy objects. - // To delete SMeta & SAcl objects: - // (1)Get all the objects in the bucket, - // (2)then all the items in each object, - // (3) then all meta & acl data for each item - Set objectsInBucket = sbucket.getObjectsInBucket(); - Iterator it = objectsInBucket.iterator(); - while( it.hasNext()) - { - SObjectVO oneObject = (SObjectVO)it.next(); - Set itemsInObject = oneObject.getItems(); - Iterator is = itemsInObject.iterator(); - while( is.hasNext()) - { - SObjectItemVO oneItem = (SObjectItemVO) is.next(); + + // Delete the file from its storage location + OrderedPair host_storagelocation_pair = getBucketStorageHost(sbucket); + S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(host_storagelocation_pair.getFirst()); + bucketAdapter.deleteContainer(host_storagelocation_pair.getSecond(), request.getBucketName()); + + // Cascade-deleting can delete related SObject/SObjectItem objects, but not SAcl, SMeta and policy objects. + // To delete SMeta & SAcl objects: + // (1)Get all the objects in the bucket, + // (2)then all the items in each object, + // (3) then all meta & acl data for each item + Set objectsInBucket = sbucket.getObjectsInBucket(); + Iterator it = objectsInBucket.iterator(); + while( it.hasNext()) + { + SObjectVO oneObject = it.next(); + Set itemsInObject = oneObject.getItems(); + Iterator is = itemsInObject.iterator(); + while( is.hasNext()) + { + SObjectItemVO oneItem = is.next(); deleteMetaData(oneItem.getId()); deleteObjectAcls("SObjectItem", oneItem.getId()); - } - } - - // Delete all the policy state associated with the bucket - try { + } + } + + // Delete all the policy state associated with the bucket + try { ServiceProvider.getInstance().deleteBucketPolicy(bucketName); bPolicyDao.deletePolicy(bucketName); - } catch( Exception e ) { - logger.error("When deleting a bucket we must try to delete its policy: ", e); - } - - deleteBucketAcls( sbucket.getId()); - bucketDao.remove(sbucket.getId()); - - - response.setResultCode(204); - response.setResultDescription("OK"); - - txn.close(); - } - else - { response.setResultCode(404); - response.setResultDescription("Bucket does not exist"); - } - return response; + } catch( Exception e ) { + logger.error("When deleting a bucket we must try to delete its policy: ", e); + } + + deleteBucketAcls( sbucket.getId()); + bucketDao.remove(sbucket.getId()); + + + response.setResultCode(204); + response.setResultDescription("OK"); + + txn.close(); + } + else + { response.setResultCode(404); + response.setResultDescription("Bucket does not exist"); + } + return response; } - + /** * Return a S3ListBucketResponse which represents a list of up to 1000 objects contained ins the bucket. * Called from S3BucketAction for GETting objects and for GETting object versions. */ - + public S3ListBucketResponse listBucketContents(S3ListBucketRequest request, boolean includeVersions) { - S3ListBucketResponse response = new S3ListBucketResponse(); - String bucketName = request.getBucketName(); - String prefix = request.getPrefix(); - if (prefix == null) prefix = StringHelper.EMPTY_STRING; - String marker = request.getMarker(); - if (marker == null) marker = StringHelper.EMPTY_STRING; - - String delimiter = request.getDelimiter(); - int maxKeys = request.getMaxKeys(); - if(maxKeys <= 0) maxKeys = 1000; - - // - SBucketVO sbucket = bucketDao.getByName(bucketName); - if (sbucket == null) throw new NoSuchObjectException("Bucket " + bucketName + " does not exist"); + S3ListBucketResponse response = new S3ListBucketResponse(); + String bucketName = request.getBucketName(); + String prefix = request.getPrefix(); + if (prefix == null) prefix = StringHelper.EMPTY_STRING; + String marker = request.getMarker(); + if (marker == null) marker = StringHelper.EMPTY_STRING; - PolicyActions action = (includeVersions ? PolicyActions.ListBucketVersions : PolicyActions.ListBucket); - S3PolicyContext context = new S3PolicyContext( action, bucketName ); - context.setEvalParam( ConditionKeys.MaxKeys, new String( "" + maxKeys )); - context.setEvalParam( ConditionKeys.Prefix, prefix ); - context.setEvalParam( ConditionKeys.Delimiter, delimiter ); - verifyAccess( context, "SBucket", sbucket.getId(), SAcl.PERMISSION_READ ); + String delimiter = request.getDelimiter(); + int maxKeys = request.getMaxKeys(); + if(maxKeys <= 0) maxKeys = 1000; - - // Wen execting the query, request one more item so that we know how to set isTruncated flag - List l = null; - - if ( includeVersions ) - l = objectDao.listAllBucketObjects( sbucket, prefix, marker, maxKeys+1 ); - else l = objectDao.listBucketObjects( sbucket, prefix, marker, maxKeys+1 ); - - response.setBucketName(bucketName); - response.setMarker(marker); - response.setMaxKeys(maxKeys); - response.setPrefix(prefix); - response.setDelimiter(delimiter); - if (null != l ) { - response.setTruncated(l.size() > maxKeys); - if(l.size() > maxKeys) { - response.setNextMarker(l.get(l.size() - 1).getNameKey()); - } - } - // If needed - SOAP response does not support versioning - response.setContents( composeListBucketContentEntries(l, prefix, delimiter, maxKeys, includeVersions, request.getVersionIdMarker())); - response.setCommonPrefixes( composeListBucketPrefixEntries(l, prefix, delimiter, maxKeys)); - return response; + // + SBucketVO sbucket = bucketDao.getByName(bucketName); + if (sbucket == null) throw new NoSuchObjectException("Bucket " + bucketName + " does not exist"); + + PolicyActions action = (includeVersions ? PolicyActions.ListBucketVersions : PolicyActions.ListBucket); + S3PolicyContext context = new S3PolicyContext( action, bucketName ); + context.setEvalParam( ConditionKeys.MaxKeys, new String( "" + maxKeys )); + context.setEvalParam( ConditionKeys.Prefix, prefix ); + context.setEvalParam( ConditionKeys.Delimiter, delimiter ); + verifyAccess( context, "SBucket", sbucket.getId(), SAcl.PERMISSION_READ ); + + + // Wen execting the query, request one more item so that we know how to set isTruncated flag + List l = null; + + if ( includeVersions ) + l = objectDao.listAllBucketObjects( sbucket, prefix, marker, maxKeys+1 ); + else l = objectDao.listBucketObjects( sbucket, prefix, marker, maxKeys+1 ); + + response.setBucketName(bucketName); + response.setMarker(marker); + response.setMaxKeys(maxKeys); + response.setPrefix(prefix); + response.setDelimiter(delimiter); + if (null != l ) { + response.setTruncated(l.size() > maxKeys); + if(l.size() > maxKeys) { + response.setNextMarker(l.get(l.size() - 1).getNameKey()); + } + } + // If needed - SOAP response does not support versioning + response.setContents( composeListBucketContentEntries(l, prefix, delimiter, maxKeys, includeVersions, request.getVersionIdMarker())); + response.setCommonPrefixes( composeListBucketPrefixEntries(l, prefix, delimiter, maxKeys)); + return response; } - + /** * Return a S3ListAllMyBucketResponse which represents a list of all buckets owned by the requester. * Called from S3BucketAction for GETting all buckets. @@ -394,90 +385,90 @@ public class S3Engine { */ public S3ListAllMyBucketsResponse handleRequest(S3ListAllMyBucketsRequest request) { - S3ListAllMyBucketsResponse response = new S3ListAllMyBucketsResponse(); + S3ListAllMyBucketsResponse response = new S3ListAllMyBucketsResponse(); - - // "...you can only list buckets for which you are the owner." - List buckets = bucketDao.listBuckets(UserContext.current().getCanonicalUserId()); - S3CanonicalUser owner = new S3CanonicalUser(); - owner.setID(UserContext.current().getCanonicalUserId()); - owner.setDisplayName(""); - response.setOwner(owner); - - if (buckets != null) - { - S3ListAllMyBucketsEntry[] entries = new S3ListAllMyBucketsEntry[buckets.size()]; - int i = 0; - for(SBucketVO bucket : buckets) - { - String bucketName = bucket.getName(); - S3PolicyContext context = new S3PolicyContext( PolicyActions.ListAllMyBuckets, bucketName ); - verifyAccess( context, "SBucket", bucket.getId(), SAcl.PERMISSION_PASS ); - - entries[i] = new S3ListAllMyBucketsEntry(); - entries[i].setName(bucketName); - entries[i].setCreationDate(DateHelper.toCalendar(bucket.getCreateTime())); - i++; - } - response.setBuckets(entries); - } - return response; + + // "...you can only list buckets for which you are the owner." + List buckets = bucketDao.listBuckets(UserContext.current().getCanonicalUserId()); + S3CanonicalUser owner = new S3CanonicalUser(); + owner.setID(UserContext.current().getCanonicalUserId()); + owner.setDisplayName(""); + response.setOwner(owner); + + if (buckets != null) + { + S3ListAllMyBucketsEntry[] entries = new S3ListAllMyBucketsEntry[buckets.size()]; + int i = 0; + for(SBucketVO bucket : buckets) + { + String bucketName = bucket.getName(); + S3PolicyContext context = new S3PolicyContext( PolicyActions.ListAllMyBuckets, bucketName ); + verifyAccess( context, "SBucket", bucket.getId(), SAcl.PERMISSION_PASS ); + + entries[i] = new S3ListAllMyBucketsEntry(); + entries[i].setName(bucketName); + entries[i].setCreationDate(DateHelper.toCalendar(bucket.getCreateTime())); + i++; + } + response.setBuckets(entries); + } + return response; } - + /** * Return an S3Response representing the result of PUTTING the ACL of a given bucket. * Called from S3BucketAction to PUT its ACL. */ - + public S3Response handleRequest(S3SetBucketAccessControlPolicyRequest request) { - S3Response response = new S3Response(); - String bucketName = request.getBucketName(); - SBucketVO sbucket = bucketDao.getByName(bucketName); - if(sbucket == null) { - response.setResultCode(404); - response.setResultDescription("Bucket does not exist"); - return response; - } - - S3PolicyContext context = new S3PolicyContext( PolicyActions.PutBucketAcl, bucketName ); - verifyAccess( context, "SBucket", sbucket.getId(), SAcl.PERMISSION_WRITE_ACL ); + S3Response response = new S3Response(); + String bucketName = request.getBucketName(); + SBucketVO sbucket = bucketDao.getByName(bucketName); + if(sbucket == null) { + response.setResultCode(404); + response.setResultDescription("Bucket does not exist"); + return response; + } - aclDao.save("SBucket", sbucket.getId(), request.getAcl()); - - response.setResultCode(200); - response.setResultDescription("OK"); - return response; + S3PolicyContext context = new S3PolicyContext( PolicyActions.PutBucketAcl, bucketName ); + verifyAccess( context, "SBucket", sbucket.getId(), SAcl.PERMISSION_WRITE_ACL ); + + aclDao.save("SBucket", sbucket.getId(), request.getAcl()); + + response.setResultCode(200); + response.setResultDescription("OK"); + return response; } - - + + /** * Return a S3AccessControlPolicy representing the ACL of a given bucket. * Called from S3BucketAction to GET its ACL. */ - + public S3AccessControlPolicy handleRequest(S3GetBucketAccessControlPolicyRequest request) { - S3AccessControlPolicy policy = new S3AccessControlPolicy(); - String bucketName = request.getBucketName(); - SBucketVO sbucket = bucketDao.getByName( bucketName ); - if (sbucket == null) - throw new NoSuchObjectException("Bucket " + bucketName + " does not exist"); - - S3CanonicalUser owner = new S3CanonicalUser(); - owner.setID(sbucket.getOwnerCanonicalId()); - owner.setDisplayName(""); - policy.setOwner(owner); - - S3PolicyContext context = new S3PolicyContext( PolicyActions.GetBucketAcl, bucketName ); - verifyAccess( context, "SBucket", sbucket.getId(), SAcl.PERMISSION_READ_ACL ); + S3AccessControlPolicy policy = new S3AccessControlPolicy(); + String bucketName = request.getBucketName(); + SBucketVO sbucket = bucketDao.getByName( bucketName ); + if (sbucket == null) + throw new NoSuchObjectException("Bucket " + bucketName + " does not exist"); + + S3CanonicalUser owner = new S3CanonicalUser(); + owner.setID(sbucket.getOwnerCanonicalId()); + owner.setDisplayName(""); + policy.setOwner(owner); + + S3PolicyContext context = new S3PolicyContext( PolicyActions.GetBucketAcl, bucketName ); + verifyAccess( context, "SBucket", sbucket.getId(), SAcl.PERMISSION_READ_ACL ); - List grants = aclDao.listGrants("SBucket", sbucket.getId()); - policy.setGrants(S3Grant.toGrants(grants)); - return policy; + List grants = aclDao.listGrants("SBucket", sbucket.getId()); + policy.setGrants(S3Grant.toGrants(grants)); + return policy; } - + /** * This method should be called if a multipart upload is aborted OR has completed successfully and * the individual parts have to be cleaned up. @@ -487,67 +478,67 @@ public class S3Engine { * @param verifyPermissiod - If false then do not check the user's permission to clean up the state */ public int freeUploadParts(String bucketName, int uploadId, boolean verifyPermission) { - - // -> we need to look up the final bucket to figure out which mount - // point to use to save the part in - // SBucketDao bucketDao = new SBucketDao(); - SBucketVO bucket = bucketDao.getByName(bucketName); - if (bucket == null) { - logger.error("initiateMultipartUpload failed since " + bucketName - + " does not exist"); - return 404; - } - OrderedPair host_storagelocation_pair = getBucketStorageHost(bucket); - S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(host_storagelocation_pair.getFirst()); + // -> we need to look up the final bucket to figure out which mount + // point to use to save the part in + // SBucketDao bucketDao = new SBucketDao(); + SBucketVO bucket = bucketDao.getByName(bucketName); + if (bucket == null) { + logger.error("initiateMultipartUpload failed since " + bucketName + + " does not exist"); + return 404; + } - try { - MultipartLoadDao uploadDao = new MultipartLoadDao(); - OrderedPair exists = uploadDao.multipartExits(uploadId); - - if (null == exists) { - logger.error("initiateMultipartUpload failed since multipart upload" - + uploadId + " does not exist"); - return 404; - } + OrderedPair host_storagelocation_pair = getBucketStorageHost(bucket); + S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(host_storagelocation_pair.getFirst()); - // -> the multipart initiator or bucket owner can do this action by - // default - if (verifyPermission) { - String initiator = uploadDao.getInitiator(uploadId); - if (null == initiator - || !initiator.equals(UserContext.current() - .getAccessKey())) { - // -> write permission on a bucket allows a PutObject / - // DeleteObject action on any object in the bucket - S3PolicyContext context = new S3PolicyContext( - PolicyActions.AbortMultipartUpload, bucketName); - context.setKeyName(exists.getSecond()); - verifyAccess(context, "SBucket", bucket.getId(), - SAcl.PERMISSION_WRITE); - } - } + try { + MultipartLoadDao uploadDao = new MultipartLoadDao(); + OrderedPair exists = uploadDao.multipartExits(uploadId); - // -> first get a list of all the uploaded files and delete one by - // one - S3MultipartPart[] parts = uploadDao.getParts(uploadId, 10000, 0); - for (int i = 0; i < parts.length; i++) { - bucketAdapter.deleteObject(host_storagelocation_pair.getSecond(), ServiceProvider.getInstance() - .getMultipartDir(), parts[i].getPath()); - } - uploadDao.deleteUpload(uploadId); - return 204; + if (null == exists) { + logger.error("initiateMultipartUpload failed since multipart upload" + + uploadId + " does not exist"); + return 404; + } - } catch (PermissionDeniedException e) { - logger.error("freeUploadParts failed due to [" + e.getMessage() - + "]", e); - throw e; - } catch (Exception e) { - logger.error("freeUploadParts failed due to [" + e.getMessage() - + "]", e); - return 500; - } - } + // -> the multipart initiator or bucket owner can do this action by + // default + if (verifyPermission) { + String initiator = uploadDao.getInitiator(uploadId); + if (null == initiator + || !initiator.equals(UserContext.current() + .getAccessKey())) { + // -> write permission on a bucket allows a PutObject / + // DeleteObject action on any object in the bucket + S3PolicyContext context = new S3PolicyContext( + PolicyActions.AbortMultipartUpload, bucketName); + context.setKeyName(exists.getSecond()); + verifyAccess(context, "SBucket", bucket.getId(), + SAcl.PERMISSION_WRITE); + } + } + + // -> first get a list of all the uploaded files and delete one by + // one + S3MultipartPart[] parts = uploadDao.getParts(uploadId, 10000, 0); + for (int i = 0; i < parts.length; i++) { + bucketAdapter.deleteObject(host_storagelocation_pair.getSecond(), ServiceProvider.getInstance() + .getMultipartDir(), parts[i].getPath()); + } + uploadDao.deleteUpload(uploadId); + return 204; + + } catch (PermissionDeniedException e) { + logger.error("freeUploadParts failed due to [" + e.getMessage() + + "]", e); + throw e; + } catch (Exception e) { + logger.error("freeUploadParts failed due to [" + e.getMessage() + + "]", e); + return 500; + } + } /** * The initiator must have permission to write to the bucket in question in order to initiate @@ -557,33 +548,33 @@ public class S3Engine { */ public S3PutObjectInlineResponse initiateMultipartUpload(S3PutObjectInlineRequest request) { - S3PutObjectInlineResponse response = new S3PutObjectInlineResponse(); - String bucketName = request.getBucketName(); - String nameKey = request.getKey(); + S3PutObjectInlineResponse response = new S3PutObjectInlineResponse(); + String bucketName = request.getBucketName(); + String nameKey = request.getKey(); - // -> does the bucket exist and can we write to it? - SBucketVO bucket = bucketDao.getByName(bucketName); - if (bucket == null) { - logger.error( "initiateMultipartUpload failed since " + bucketName + " does not exist" ); - response.setResultCode(404); - } - - S3PolicyContext context = new S3PolicyContext( PolicyActions.PutObject, bucketName ); - context.setKeyName( nameKey ); - context.setEvalParam( ConditionKeys.Acl, request.getCannedAccess()); - verifyAccess( context, "SBucket", bucket.getId(), SAcl.PERMISSION_WRITE ); + // -> does the bucket exist and can we write to it? + SBucketVO bucket = bucketDao.getByName(bucketName); + if (bucket == null) { + logger.error( "initiateMultipartUpload failed since " + bucketName + " does not exist" ); + response.setResultCode(404); + } - createUploadFolder( bucketName ); + S3PolicyContext context = new S3PolicyContext( PolicyActions.PutObject, bucketName ); + context.setKeyName( nameKey ); + context.setEvalParam( ConditionKeys.Acl, request.getCannedAccess()); + verifyAccess( context, "SBucket", bucket.getId(), SAcl.PERMISSION_WRITE ); + + createUploadFolder( bucketName ); try { - MultipartLoadDao uploadDao = new MultipartLoadDao(); - int uploadId = uploadDao.initiateUpload( UserContext.current().getAccessKey(), bucketName, nameKey, request.getCannedAccess(), request.getMetaEntries()); - response.setUploadId( uploadId ); - response.setResultCode(200); - + MultipartLoadDao uploadDao = new MultipartLoadDao(); + int uploadId = uploadDao.initiateUpload( UserContext.current().getAccessKey(), bucketName, nameKey, request.getCannedAccess(), request.getMetaEntries()); + response.setUploadId( uploadId ); + response.setResultCode(200); + } catch( Exception e ) { logger.error("initiateMultipartUpload exception: ", e); - response.setResultCode(500); + response.setResultCode(500); } return response; @@ -600,55 +591,55 @@ public class S3Engine { */ public S3PutObjectInlineResponse saveUploadPart(S3PutObjectInlineRequest request, int uploadId, int partNumber) { - S3PutObjectInlineResponse response = new S3PutObjectInlineResponse(); - String bucketName = request.getBucketName(); + S3PutObjectInlineResponse response = new S3PutObjectInlineResponse(); + String bucketName = request.getBucketName(); - // -> we need to look up the final bucket to figure out which mount point to use to save the part in - //SBucketDao bucketDao = new SBucketDao(); - SBucketVO bucket = bucketDao.getByName(bucketName); - if (bucket == null) { - logger.error( "saveUploadedPart failed since " + bucketName + " does not exist" ); - response.setResultCode(404); - } - S3PolicyContext context = new S3PolicyContext( PolicyActions.PutObject, bucketName ); - context.setKeyName( request.getKey()); - verifyAccess( context, "SBucket", bucket.getId(), SAcl.PERMISSION_WRITE ); - - OrderedPair host_storagelocation_pair = getBucketStorageHost(bucket); - S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(host_storagelocation_pair.getFirst()); - String itemFileName = new String( uploadId + "-" + partNumber ); - InputStream is = null; + // -> we need to look up the final bucket to figure out which mount point to use to save the part in + //SBucketDao bucketDao = new SBucketDao(); + SBucketVO bucket = bucketDao.getByName(bucketName); + if (bucket == null) { + logger.error( "saveUploadedPart failed since " + bucketName + " does not exist" ); + response.setResultCode(404); + } + S3PolicyContext context = new S3PolicyContext( PolicyActions.PutObject, bucketName ); + context.setKeyName( request.getKey()); + verifyAccess( context, "SBucket", bucket.getId(), SAcl.PERMISSION_WRITE ); - try { - is = request.getDataInputStream(); - String md5Checksum = bucketAdapter.saveObject(is, host_storagelocation_pair.getSecond(), ServiceProvider.getInstance().getMultipartDir(), itemFileName); - response.setETag(md5Checksum); - MultipartLoadDao uploadDao = new MultipartLoadDao(); - uploadDao.savePart(uploadId, partNumber, md5Checksum, itemFileName,(int) request.getContentLength()); - response.setResultCode(200); - - } catch (IOException e) { - logger.error("UploadPart failed due to " + e.getMessage(), e); - response.setResultCode(500); - } catch (OutOfStorageException e) { - logger.error("UploadPart failed due to " + e.getMessage(), e); - response.setResultCode(500); - } catch (Exception e) { - logger.error("UploadPart failed due to " + e.getMessage(), e); - response.setResultCode(500); - } finally { - if(is != null) { - try { - is.close(); - } catch (IOException e) { - logger.error("UploadPart unable to close stream from data handler.", e); - } - } - } - - return response; + OrderedPair host_storagelocation_pair = getBucketStorageHost(bucket); + S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(host_storagelocation_pair.getFirst()); + String itemFileName = new String( uploadId + "-" + partNumber ); + InputStream is = null; + + try { + is = request.getDataInputStream(); + String md5Checksum = bucketAdapter.saveObject(is, host_storagelocation_pair.getSecond(), ServiceProvider.getInstance().getMultipartDir(), itemFileName); + response.setETag(md5Checksum); + MultipartLoadDao uploadDao = new MultipartLoadDao(); + uploadDao.savePart(uploadId, partNumber, md5Checksum, itemFileName,(int) request.getContentLength()); + response.setResultCode(200); + + } catch (IOException e) { + logger.error("UploadPart failed due to " + e.getMessage(), e); + response.setResultCode(500); + } catch (OutOfStorageException e) { + logger.error("UploadPart failed due to " + e.getMessage(), e); + response.setResultCode(500); + } catch (Exception e) { + logger.error("UploadPart failed due to " + e.getMessage(), e); + response.setResultCode(500); + } finally { + if(is != null) { + try { + is.close(); + } catch (IOException e) { + logger.error("UploadPart unable to close stream from data handler.", e); + } + } + } + + return response; } - + /** * Create the real object represented by all the parts of the multipart upload. * Called from S3ObjectAction at completion of multipart upload. @@ -659,55 +650,55 @@ public class S3Engine { * N.B. - This method can be long-lasting * We are required to keep the connection alive by returning whitespace characters back periodically. */ - + public S3PutObjectInlineResponse concatentateMultipartUploads(HttpServletResponse httpResp, S3PutObjectInlineRequest request, S3MultipartPart[] parts, OutputStream outputStream) throws IOException { - // [A] Set up and initial error checking - S3PutObjectInlineResponse response = new S3PutObjectInlineResponse(); - String bucketName = request.getBucketName(); - String key = request.getKey(); - S3MetaDataEntry[] meta = request.getMetaEntries(); + // [A] Set up and initial error checking + S3PutObjectInlineResponse response = new S3PutObjectInlineResponse(); + String bucketName = request.getBucketName(); + String key = request.getKey(); + S3MetaDataEntry[] meta = request.getMetaEntries(); - SBucketVO bucket = bucketDao.getByName(bucketName); - if (bucket == null) { - logger.error("completeMultipartUpload( failed since " + bucketName - + " does not exist"); - response.setResultCode(404); - } + SBucketVO bucket = bucketDao.getByName(bucketName); + if (bucket == null) { + logger.error("completeMultipartUpload( failed since " + bucketName + + " does not exist"); + response.setResultCode(404); + } - // [B] Now we need to create the final re-assembled object - // -> the allocObjectItem checks for the bucket policy PutObject - // permissions - OrderedPair object_objectitem_pair = allocObjectItem( - bucket, key, meta, null, request.getCannedAccess()); - OrderedPair host_storagelocation_pair = getBucketStorageHost(bucket); + // [B] Now we need to create the final re-assembled object + // -> the allocObjectItem checks for the bucket policy PutObject + // permissions + OrderedPair object_objectitem_pair = allocObjectItem( + bucket, key, meta, null, request.getCannedAccess()); + OrderedPair host_storagelocation_pair = getBucketStorageHost(bucket); - S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(host_storagelocation_pair - .getFirst()); - String itemFileName = object_objectitem_pair.getSecond() - .getStoredPath(); + S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(host_storagelocation_pair + .getFirst()); + String itemFileName = object_objectitem_pair.getSecond() + .getStoredPath(); + + // -> Amazon defines that we must return a 200 response immediately to + // the client, but + // -> we don't know the version header until we hit here + httpResp.setStatus(200); + httpResp.setContentType("text/xml; charset=UTF-8"); + String version = object_objectitem_pair.getSecond().getVersion(); + if (null != version) + httpResp.addHeader("x-amz-version-id", version); + httpResp.flushBuffer(); + Transaction txn = Transaction.open(Transaction.AWSAPI_DB); + // [C] Re-assemble the object from its uploaded file parts + try { + // explicit transaction control to avoid holding transaction during + // long file concatenation process + txn.start(); + OrderedPair result = bucketAdapter + .concatentateObjects(host_storagelocation_pair.getSecond(), + bucket.getName(), itemFileName, ServiceProvider + .getInstance().getMultipartDir(), parts, + outputStream); - // -> Amazon defines that we must return a 200 response immediately to - // the client, but - // -> we don't know the version header until we hit here - httpResp.setStatus(200); - httpResp.setContentType("text/xml; charset=UTF-8"); - String version = object_objectitem_pair.getSecond().getVersion(); - if (null != version) - httpResp.addHeader("x-amz-version-id", version); - httpResp.flushBuffer(); - Transaction txn = Transaction.open(Transaction.AWSAPI_DB); - // [C] Re-assemble the object from its uploaded file parts - try { - // explicit transaction control to avoid holding transaction during - // long file concatenation process - txn.start(); - OrderedPair result = bucketAdapter - .concatentateObjects(host_storagelocation_pair.getSecond(), - bucket.getName(), itemFileName, ServiceProvider - .getInstance().getMultipartDir(), parts, - outputStream); - response.setETag(result.getFirst()); response.setLastModified(DateHelper.toCalendar(object_objectitem_pair.getSecond().getLastModifiedTime())); SObjectItemVO item = itemDao.findById(object_objectitem_pair @@ -716,13 +707,13 @@ public class S3Engine { item.setStoredSize(result.getSecond().longValue()); itemDao.update(item.getId(), item); response.setResultCode(200); - } catch (Exception e) { - logger.error("completeMultipartUpload failed due to " + e.getMessage(),e); - txn.close(); - } - return response; + } catch (Exception e) { + logger.error("completeMultipartUpload failed due to " + e.getMessage(),e); + txn.close(); + } + return response; } - + /** * Return a S3PutObjectInlineResponse which represents an object being created into a bucket * Called from S3ObjectAction when PUTting or POTing an object. @@ -730,61 +721,61 @@ public class S3Engine { @DB public S3PutObjectInlineResponse handleRequest(S3PutObjectInlineRequest request) { - S3PutObjectInlineResponse response = new S3PutObjectInlineResponse(); - String bucketName = request.getBucketName(); - String key = request.getKey(); - long contentLength = request.getContentLength(); - S3MetaDataEntry[] meta = request.getMetaEntries(); - S3AccessControlList acl = request.getAcl(); - - SBucketVO bucket = bucketDao.getByName(bucketName); - if (bucket == null) throw new NoSuchObjectException("Bucket " + bucketName + " does not exist"); - + S3PutObjectInlineResponse response = new S3PutObjectInlineResponse(); + String bucketName = request.getBucketName(); + String key = request.getKey(); + long contentLength = request.getContentLength(); + S3MetaDataEntry[] meta = request.getMetaEntries(); + S3AccessControlList acl = request.getAcl(); - // Is the caller allowed to write the object? - // The allocObjectItem checks for the bucket policy PutObject permissions - OrderedPair object_objectitem_pair = allocObjectItem(bucket, key, meta, acl, request.getCannedAccess()); - OrderedPair host_storagelocation_pair = getBucketStorageHost(bucket); - - S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(host_storagelocation_pair.getFirst()); - String itemFileName = object_objectitem_pair.getSecond().getStoredPath(); - InputStream is = null; - Transaction txn = null; - try { - // explicit transaction control to avoid holding transaction during file-copy process - - txn = Transaction.open(Transaction.AWSAPI_DB); - txn.start(); - is = request.getDataInputStream(); - String md5Checksum = bucketAdapter.saveObject(is, host_storagelocation_pair.getSecond(), bucket.getName(), itemFileName); - response.setETag(md5Checksum); - response.setLastModified(DateHelper.toCalendar( object_objectitem_pair.getSecond().getLastModifiedTime())); - response.setVersion( object_objectitem_pair.getSecond().getVersion()); - - //SObjectItemDaoImpl itemDao = new SObjectItemDaoImpl(); - SObjectItemVO item = itemDao.findById(object_objectitem_pair.getSecond().getId()); - item.setMd5(md5Checksum); - item.setStoredSize(contentLength); - itemDao.update(item.getId(), item); - txn.commit(); - } catch (IOException e) { - logger.error("PutObjectInline failed due to " + e.getMessage(), e); - } catch (OutOfStorageException e) { - logger.error("PutObjectInline failed due to " + e.getMessage(), e); - } finally { - if(is != null) { - try { - is.close(); - } catch (IOException e) { - logger.error("PutObjectInline unable to close stream from data handler.", e); - } - } - txn.close(); - } - - return response; + SBucketVO bucket = bucketDao.getByName(bucketName); + if (bucket == null) throw new NoSuchObjectException("Bucket " + bucketName + " does not exist"); + + + // Is the caller allowed to write the object? + // The allocObjectItem checks for the bucket policy PutObject permissions + OrderedPair object_objectitem_pair = allocObjectItem(bucket, key, meta, acl, request.getCannedAccess()); + OrderedPair host_storagelocation_pair = getBucketStorageHost(bucket); + + S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(host_storagelocation_pair.getFirst()); + String itemFileName = object_objectitem_pair.getSecond().getStoredPath(); + InputStream is = null; + Transaction txn = null; + try { + // explicit transaction control to avoid holding transaction during file-copy process + + txn = Transaction.open(Transaction.AWSAPI_DB); + txn.start(); + is = request.getDataInputStream(); + String md5Checksum = bucketAdapter.saveObject(is, host_storagelocation_pair.getSecond(), bucket.getName(), itemFileName); + response.setETag(md5Checksum); + response.setLastModified(DateHelper.toCalendar( object_objectitem_pair.getSecond().getLastModifiedTime())); + response.setVersion( object_objectitem_pair.getSecond().getVersion()); + + //SObjectItemDaoImpl itemDao = new SObjectItemDaoImpl(); + SObjectItemVO item = itemDao.findById(object_objectitem_pair.getSecond().getId()); + item.setMd5(md5Checksum); + item.setStoredSize(contentLength); + itemDao.update(item.getId(), item); + txn.commit(); + } catch (IOException e) { + logger.error("PutObjectInline failed due to " + e.getMessage(), e); + } catch (OutOfStorageException e) { + logger.error("PutObjectInline failed due to " + e.getMessage(), e); + } finally { + if(is != null) { + try { + is.close(); + } catch (IOException e) { + logger.error("PutObjectInline unable to close stream from data handler.", e); + } + } + txn.close(); + } + + return response; } - + /** * Return a S3PutObjectResponse which represents an object being created into a bucket * Called from S3RestServlet when processing a DIME request. @@ -792,56 +783,56 @@ public class S3Engine { public S3PutObjectResponse handleRequest(S3PutObjectRequest request) { - S3PutObjectResponse response = new S3PutObjectResponse(); - String bucketName = request.getBucketName(); - String key = request.getKey(); - long contentLength = request.getContentLength(); - S3MetaDataEntry[] meta = request.getMetaEntries(); - S3AccessControlList acl = request.getAcl(); - - SBucketVO bucket = bucketDao.getByName(bucketName); - if(bucket == null) throw new NoSuchObjectException("Bucket " + bucketName + " does not exist"); - - // Is the caller allowed to write the object? - // The allocObjectItem checks for the bucket policy PutObject permissions - OrderedPair object_objectitem_pair = allocObjectItem(bucket, key, meta, acl, null); - OrderedPair host_storagelocation_pair = getBucketStorageHost(bucket); - - S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(host_storagelocation_pair.getFirst()); - String itemFileName = object_objectitem_pair.getSecond().getStoredPath(); - InputStream is = null; - Transaction txn = null; - try { - // explicit transaction control to avoid holding transaction during file-copy process - - txn = Transaction.open(Transaction.AWSAPI_DB); - txn.start(); - - is = request.getInputStream(); - String md5Checksum = bucketAdapter.saveObject(is, host_storagelocation_pair.getSecond(), bucket.getName(), itemFileName); - response.setETag(md5Checksum); - response.setLastModified(DateHelper.toCalendar( object_objectitem_pair.getSecond().getLastModifiedTime())); - - SObjectItemVO item = itemDao.findById(object_objectitem_pair.getSecond().getId()); - item.setMd5(md5Checksum); - item.setStoredSize(contentLength); - itemDao.update(item.getId(), item); - txn.commit(); - - } catch (OutOfStorageException e) { - logger.error("PutObject failed due to " + e.getMessage(), e); - } finally { - if(is != null) { - try { - is.close(); - } catch (IOException e) { - logger.error("Unable to close stream from data handler.", e); - } - } - txn.close(); - } - - return response; + S3PutObjectResponse response = new S3PutObjectResponse(); + String bucketName = request.getBucketName(); + String key = request.getKey(); + long contentLength = request.getContentLength(); + S3MetaDataEntry[] meta = request.getMetaEntries(); + S3AccessControlList acl = request.getAcl(); + + SBucketVO bucket = bucketDao.getByName(bucketName); + if(bucket == null) throw new NoSuchObjectException("Bucket " + bucketName + " does not exist"); + + // Is the caller allowed to write the object? + // The allocObjectItem checks for the bucket policy PutObject permissions + OrderedPair object_objectitem_pair = allocObjectItem(bucket, key, meta, acl, null); + OrderedPair host_storagelocation_pair = getBucketStorageHost(bucket); + + S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(host_storagelocation_pair.getFirst()); + String itemFileName = object_objectitem_pair.getSecond().getStoredPath(); + InputStream is = null; + Transaction txn = null; + try { + // explicit transaction control to avoid holding transaction during file-copy process + + txn = Transaction.open(Transaction.AWSAPI_DB); + txn.start(); + + is = request.getInputStream(); + String md5Checksum = bucketAdapter.saveObject(is, host_storagelocation_pair.getSecond(), bucket.getName(), itemFileName); + response.setETag(md5Checksum); + response.setLastModified(DateHelper.toCalendar( object_objectitem_pair.getSecond().getLastModifiedTime())); + + SObjectItemVO item = itemDao.findById(object_objectitem_pair.getSecond().getId()); + item.setMd5(md5Checksum); + item.setStoredSize(contentLength); + itemDao.update(item.getId(), item); + txn.commit(); + + } catch (OutOfStorageException e) { + logger.error("PutObject failed due to " + e.getMessage(), e); + } finally { + if(is != null) { + try { + is.close(); + } catch (IOException e) { + logger.error("Unable to close stream from data handler.", e); + } + } + txn.close(); + } + + return response; } /** @@ -849,795 +840,795 @@ public class S3Engine { * version of an object. To set the ACL of a different version, using the versionId subresource. * Called from S3ObjectAction to PUT an object's ACL. */ - + public S3Response handleRequest(S3SetObjectAccessControlPolicyRequest request) { - S3PolicyContext context = null; - - // [A] First find the object in the bucket - S3Response response = new S3Response(); - String bucketName = request.getBucketName(); - SBucketVO sbucket = bucketDao.getByName( bucketName ); - if(sbucket == null) { - response.setResultCode(404); - response.setResultDescription("Bucket " + bucketName + "does not exist"); - return response; - } - - String nameKey = request.getKey(); - SObjectVO sobject = objectDao.getByNameKey( sbucket, nameKey ); - if(sobject == null) { - response.setResultCode(404); - response.setResultDescription("Object " + request.getKey() + " in bucket " + bucketName + " does not exist"); - return response; - } - - String deletionMark = sobject.getDeletionMark(); - if (null != deletionMark) { - response.setResultCode(404); - response.setResultDescription("Object " + request.getKey() + " has been deleted (1)"); - return response; - } - + S3PolicyContext context = null; - // [B] Versioning allow the client to ask for a specific version not just the latest - SObjectItemVO item = null; + // [A] First find the object in the bucket + S3Response response = new S3Response(); + String bucketName = request.getBucketName(); + SBucketVO sbucket = bucketDao.getByName( bucketName ); + if(sbucket == null) { + response.setResultCode(404); + response.setResultDescription("Bucket " + bucketName + "does not exist"); + return response; + } + + String nameKey = request.getKey(); + SObjectVO sobject = objectDao.getByNameKey( sbucket, nameKey ); + if(sobject == null) { + response.setResultCode(404); + response.setResultDescription("Object " + request.getKey() + " in bucket " + bucketName + " does not exist"); + return response; + } + + String deletionMark = sobject.getDeletionMark(); + if (null != deletionMark) { + response.setResultCode(404); + response.setResultDescription("Object " + request.getKey() + " has been deleted (1)"); + return response; + } + + + // [B] Versioning allow the client to ask for a specific version not just the latest + SObjectItemVO item = null; int versioningStatus = sbucket.getVersioningStatus(); - String wantVersion = request.getVersion(); - if ( SBucket.VERSIONING_ENABLED == versioningStatus && null != wantVersion) - item = sobject.getVersion( wantVersion ); - else item = sobject.getLatestVersion(( SBucket.VERSIONING_ENABLED != versioningStatus )); - - if (item == null) { - response.setResultCode(404); - response.setResultDescription("Object " + request.getKey() + " has been deleted (2)"); - return response; - } + String wantVersion = request.getVersion(); + if ( SBucket.VERSIONING_ENABLED == versioningStatus && null != wantVersion) + item = sobject.getVersion( wantVersion ); + else item = sobject.getLatestVersion(( SBucket.VERSIONING_ENABLED != versioningStatus )); - if ( SBucket.VERSIONING_ENABLED == versioningStatus ) { - context = new S3PolicyContext( PolicyActions.PutObjectAclVersion, bucketName ); - context.setEvalParam( ConditionKeys.VersionId, wantVersion ); - response.setVersion( item.getVersion()); - } - else context = new S3PolicyContext( PolicyActions.PutObjectAcl, bucketName ); - context.setKeyName( nameKey ); - verifyAccess( context, "SObjectItem", item.getId(), SAcl.PERMISSION_WRITE_ACL ); + if (item == null) { + response.setResultCode(404); + response.setResultDescription("Object " + request.getKey() + " has been deleted (2)"); + return response; + } - // -> the acl always goes on the instance of the object - aclDao.save("SObjectItem", item.getId(), request.getAcl()); - - response.setResultCode(200); - response.setResultDescription("OK"); - return response; + if ( SBucket.VERSIONING_ENABLED == versioningStatus ) { + context = new S3PolicyContext( PolicyActions.PutObjectAclVersion, bucketName ); + context.setEvalParam( ConditionKeys.VersionId, wantVersion ); + response.setVersion( item.getVersion()); + } + else context = new S3PolicyContext( PolicyActions.PutObjectAcl, bucketName ); + context.setKeyName( nameKey ); + verifyAccess( context, "SObjectItem", item.getId(), SAcl.PERMISSION_WRITE_ACL ); + + // -> the acl always goes on the instance of the object + aclDao.save("SObjectItem", item.getId(), request.getAcl()); + + response.setResultCode(200); + response.setResultDescription("OK"); + return response; } - + /** * By default, GET returns ACL information about the latest version of an object. To return ACL * information about a different version, use the versionId subresource * Called from S3ObjectAction to get an object's ACL. */ - + public S3AccessControlPolicy handleRequest(S3GetObjectAccessControlPolicyRequest request) { - S3PolicyContext context = null; + S3PolicyContext context = null; - // [A] Does the object exist that holds the ACL we are looking for? - S3AccessControlPolicy policy = new S3AccessControlPolicy(); - - String bucketName = request.getBucketName(); - SBucketVO sbucket = bucketDao.getByName( bucketName ); - if (sbucket == null) - throw new NoSuchObjectException("Bucket " + bucketName + " does not exist"); - - //SObjectDaoImpl sobjectDao = new SObjectDaoImpl(); - String nameKey = request.getKey(); - SObjectVO sobject = objectDao.getByNameKey( sbucket, nameKey ); - if (sobject == null) - throw new NoSuchObjectException("Object " + request.getKey() + " does not exist"); - - String deletionMark = sobject.getDeletionMark(); - if (null != deletionMark) { - policy.setResultCode(404); - policy.setResultDescription("Object " + request.getKey() + " has been deleted (1)"); - return policy; - } - + // [A] Does the object exist that holds the ACL we are looking for? + S3AccessControlPolicy policy = new S3AccessControlPolicy(); - // [B] Versioning allow the client to ask for a specific version not just the latest - SObjectItemVO item = null; + String bucketName = request.getBucketName(); + SBucketVO sbucket = bucketDao.getByName( bucketName ); + if (sbucket == null) + throw new NoSuchObjectException("Bucket " + bucketName + " does not exist"); + + //SObjectDaoImpl sobjectDao = new SObjectDaoImpl(); + String nameKey = request.getKey(); + SObjectVO sobject = objectDao.getByNameKey( sbucket, nameKey ); + if (sobject == null) + throw new NoSuchObjectException("Object " + request.getKey() + " does not exist"); + + String deletionMark = sobject.getDeletionMark(); + if (null != deletionMark) { + policy.setResultCode(404); + policy.setResultDescription("Object " + request.getKey() + " has been deleted (1)"); + return policy; + } + + + // [B] Versioning allow the client to ask for a specific version not just the latest + SObjectItemVO item = null; int versioningStatus = sbucket.getVersioningStatus(); - String wantVersion = request.getVersion(); - if ( SBucket.VERSIONING_ENABLED == versioningStatus && null != wantVersion) - item = sobject.getVersion( wantVersion ); - else item = sobject.getLatestVersion(( SBucket.VERSIONING_ENABLED != versioningStatus )); - - if (item == null) { - policy.setResultCode(404); - policy.setResultDescription("Object " + request.getKey() + " has been deleted (2)"); - return policy; - } + String wantVersion = request.getVersion(); + if ( SBucket.VERSIONING_ENABLED == versioningStatus && null != wantVersion) + item = sobject.getVersion( wantVersion ); + else item = sobject.getLatestVersion(( SBucket.VERSIONING_ENABLED != versioningStatus )); + + if (item == null) { + policy.setResultCode(404); + policy.setResultDescription("Object " + request.getKey() + " has been deleted (2)"); + return policy; + } + + if ( SBucket.VERSIONING_ENABLED == versioningStatus ) { + context = new S3PolicyContext( PolicyActions.GetObjectVersionAcl, bucketName ); + context.setEvalParam( ConditionKeys.VersionId, wantVersion ); + policy.setVersion( item.getVersion()); + } + else context = new S3PolicyContext( PolicyActions.GetObjectAcl, bucketName ); + context.setKeyName( nameKey ); + verifyAccess( context, "SObjectItem", item.getId(), SAcl.PERMISSION_READ_ACL ); - if ( SBucket.VERSIONING_ENABLED == versioningStatus ) { - context = new S3PolicyContext( PolicyActions.GetObjectVersionAcl, bucketName ); - context.setEvalParam( ConditionKeys.VersionId, wantVersion ); - policy.setVersion( item.getVersion()); - } - else context = new S3PolicyContext( PolicyActions.GetObjectAcl, bucketName ); - context.setKeyName( nameKey ); - verifyAccess( context, "SObjectItem", item.getId(), SAcl.PERMISSION_READ_ACL ); - // [C] ACLs are ALWAYS on an instance of the object - S3CanonicalUser owner = new S3CanonicalUser(); - owner.setID(sobject.getOwnerCanonicalId()); - owner.setDisplayName(""); - policy.setOwner(owner); - policy.setResultCode(200); - - - List grants = aclDao.listGrants( "SObjectItem", item.getId()); - policy.setGrants(S3Grant.toGrants(grants)); - return policy; + S3CanonicalUser owner = new S3CanonicalUser(); + owner.setID(sobject.getOwnerCanonicalId()); + owner.setDisplayName(""); + policy.setOwner(owner); + policy.setResultCode(200); + + + List grants = aclDao.listGrants( "SObjectItem", item.getId()); + policy.setGrants(S3Grant.toGrants(grants)); + return policy; } - + /** * Handle requests for GET object and HEAD "get object extended" * Called from S3ObjectAction for GET and HEAD of an object. */ - + public S3GetObjectResponse handleRequest(S3GetObjectRequest request) { - S3GetObjectResponse response = new S3GetObjectResponse(); - S3PolicyContext context = null; - boolean ifRange = false; - long bytesStart = request.getByteRangeStart(); - long bytesEnd = request.getByteRangeEnd(); - int resultCode = 200; + S3GetObjectResponse response = new S3GetObjectResponse(); + S3PolicyContext context = null; + boolean ifRange = false; + long bytesStart = request.getByteRangeStart(); + long bytesEnd = request.getByteRangeEnd(); + int resultCode = 200; - // [A] Verify that the bucket and the object exist - - String bucketName = request.getBucketName(); - SBucketVO sbucket = bucketDao.getByName(bucketName); - if (sbucket == null) { - response.setResultCode(404); - response.setResultDescription("Bucket " + request.getBucketName() + " does not exist"); - return response; - } + // [A] Verify that the bucket and the object exist - String nameKey = request.getKey(); - SObjectVO sobject = objectDao.getByNameKey( sbucket, nameKey ); - if (sobject == null) { - response.setResultCode(404); - response.setResultDescription("Object " + request.getKey() + " does not exist in bucket " + request.getBucketName()); - return response; - } - - String deletionMark = sobject.getDeletionMark(); - if (null != deletionMark) { - response.setDeleteMarker( deletionMark ); - response.setResultCode(404); - response.setResultDescription("Object " + request.getKey() + " has been deleted (1)"); - return response; - } - + String bucketName = request.getBucketName(); + SBucketVO sbucket = bucketDao.getByName(bucketName); + if (sbucket == null) { + response.setResultCode(404); + response.setResultDescription("Bucket " + request.getBucketName() + " does not exist"); + return response; + } - // [B] Versioning allow the client to ask for a specific version not just the latest - SObjectItemVO item = null; + String nameKey = request.getKey(); + SObjectVO sobject = objectDao.getByNameKey( sbucket, nameKey ); + if (sobject == null) { + response.setResultCode(404); + response.setResultDescription("Object " + request.getKey() + " does not exist in bucket " + request.getBucketName()); + return response; + } + + String deletionMark = sobject.getDeletionMark(); + if (null != deletionMark) { + response.setDeleteMarker( deletionMark ); + response.setResultCode(404); + response.setResultDescription("Object " + request.getKey() + " has been deleted (1)"); + return response; + } + + + // [B] Versioning allow the client to ask for a specific version not just the latest + SObjectItemVO item = null; int versioningStatus = sbucket.getVersioningStatus(); - String wantVersion = request.getVersion(); - if ( SBucket.VERSIONING_ENABLED == versioningStatus && null != wantVersion) - item = sobject.getVersion( wantVersion ); - else item = sobject.getLatestVersion(( SBucket.VERSIONING_ENABLED != versioningStatus )); - - if (item == null) { - response.setResultCode(404); - response.setResultDescription("Object " + request.getKey() + " has been deleted (2)"); - return response; - } - - if ( SBucket.VERSIONING_ENABLED == versioningStatus ) { - context = new S3PolicyContext( PolicyActions.GetObjectVersion, bucketName ); - context.setEvalParam( ConditionKeys.VersionId, wantVersion ); - } - else context = new S3PolicyContext( PolicyActions.GetObject, bucketName ); - context.setKeyName( nameKey ); - verifyAccess( context, "SObjectItem", item.getId(), SAcl.PERMISSION_READ ); - - - // [C] Handle all the IFModifiedSince ... conditions, and access privileges - // -> http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.27 (HTTP If-Range header) - if (request.isReturnCompleteObjectOnConditionFailure() && (0 <= bytesStart && 0 <= bytesEnd)) ifRange = true; + String wantVersion = request.getVersion(); + if ( SBucket.VERSIONING_ENABLED == versioningStatus && null != wantVersion) + item = sobject.getVersion( wantVersion ); + else item = sobject.getLatestVersion(( SBucket.VERSIONING_ENABLED != versioningStatus )); - resultCode = conditionPassed( request.getConditions(), item.getLastModifiedTime(), item.getMd5(), ifRange ); - if ( -1 == resultCode ) { - // -> If-Range implementation, we have to return the entire object - resultCode = 200; - bytesStart = -1; - bytesEnd = -1; - } - else if (200 != resultCode) { - response.setResultCode( resultCode ); - response.setResultDescription( "Precondition Failed" ); - return response; - } + if (item == null) { + response.setResultCode(404); + response.setResultDescription("Object " + request.getKey() + " has been deleted (2)"); + return response; + } + + if ( SBucket.VERSIONING_ENABLED == versioningStatus ) { + context = new S3PolicyContext( PolicyActions.GetObjectVersion, bucketName ); + context.setEvalParam( ConditionKeys.VersionId, wantVersion ); + } + else context = new S3PolicyContext( PolicyActions.GetObject, bucketName ); + context.setKeyName( nameKey ); + verifyAccess( context, "SObjectItem", item.getId(), SAcl.PERMISSION_READ ); - // [D] Return the contents of the object inline - // -> extract the meta data that corresponds the specific versioned item + // [C] Handle all the IFModifiedSince ... conditions, and access privileges + // -> http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.27 (HTTP If-Range header) + if (request.isReturnCompleteObjectOnConditionFailure() && (0 <= bytesStart && 0 <= bytesEnd)) ifRange = true; - List itemMetaData = metaDao.getByTarget( "SObjectItem", item.getId()); - if (null != itemMetaData) - { - int i = 0; - S3MetaDataEntry[] metaEntries = new S3MetaDataEntry[ itemMetaData.size() ]; - ListIterator it = itemMetaData.listIterator(); - while( it.hasNext()) { - SMetaVO oneTag = (SMetaVO)it.next(); - S3MetaDataEntry oneEntry = new S3MetaDataEntry(); - oneEntry.setName( oneTag.getName()); - oneEntry.setValue( oneTag.getValue()); - metaEntries[i++] = oneEntry; - } - response.setMetaEntries( metaEntries ); - } - - // -> support a single byte range - if ( 0 <= bytesStart && 0 <= bytesEnd ) { - response.setContentLength( bytesEnd - bytesStart ); - resultCode = 206; - } - else response.setContentLength( item.getStoredSize()); - - if(request.isReturnData()) - { - response.setETag(item.getMd5()); - response.setLastModified(DateHelper.toCalendar( item.getLastModifiedTime())); - response.setVersion( item.getVersion()); - if (request.isInlineData()) - { - OrderedPair tupleSHostInfo = getBucketStorageHost(sbucket); - S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(tupleSHostInfo.getFirst()); - - if ( 0 <= bytesStart && 0 <= bytesEnd ) - response.setData(bucketAdapter.loadObjectRange(tupleSHostInfo.getSecond(), - request.getBucketName(), item.getStoredPath(), bytesStart, bytesEnd )); - else response.setData(bucketAdapter.loadObject(tupleSHostInfo.getSecond(), request.getBucketName(), item.getStoredPath())); - } - } - - response.setResultCode( resultCode ); - response.setResultDescription("OK"); - return response; + resultCode = conditionPassed( request.getConditions(), item.getLastModifiedTime(), item.getMd5(), ifRange ); + if ( -1 == resultCode ) { + // -> If-Range implementation, we have to return the entire object + resultCode = 200; + bytesStart = -1; + bytesEnd = -1; + } + else if (200 != resultCode) { + response.setResultCode( resultCode ); + response.setResultDescription( "Precondition Failed" ); + return response; + } + + + // [D] Return the contents of the object inline + // -> extract the meta data that corresponds the specific versioned item + + List itemMetaData = metaDao.getByTarget( "SObjectItem", item.getId()); + if (null != itemMetaData) + { + int i = 0; + S3MetaDataEntry[] metaEntries = new S3MetaDataEntry[ itemMetaData.size() ]; + ListIterator it = itemMetaData.listIterator(); + while( it.hasNext()) { + SMetaVO oneTag = it.next(); + S3MetaDataEntry oneEntry = new S3MetaDataEntry(); + oneEntry.setName( oneTag.getName()); + oneEntry.setValue( oneTag.getValue()); + metaEntries[i++] = oneEntry; + } + response.setMetaEntries( metaEntries ); + } + + // -> support a single byte range + if ( 0 <= bytesStart && 0 <= bytesEnd ) { + response.setContentLength( bytesEnd - bytesStart ); + resultCode = 206; + } + else response.setContentLength( item.getStoredSize()); + + if(request.isReturnData()) + { + response.setETag(item.getMd5()); + response.setLastModified(DateHelper.toCalendar( item.getLastModifiedTime())); + response.setVersion( item.getVersion()); + if (request.isInlineData()) + { + OrderedPair tupleSHostInfo = getBucketStorageHost(sbucket); + S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(tupleSHostInfo.getFirst()); + + if ( 0 <= bytesStart && 0 <= bytesEnd ) + response.setData(bucketAdapter.loadObjectRange(tupleSHostInfo.getSecond(), + request.getBucketName(), item.getStoredPath(), bytesStart, bytesEnd )); + else response.setData(bucketAdapter.loadObject(tupleSHostInfo.getSecond(), request.getBucketName(), item.getStoredPath())); + } + } + + response.setResultCode( resultCode ); + response.setResultDescription("OK"); + return response; } - + /** * Handle object deletion requests, both versioning and non-versioning requirements. * Called from S3ObjectAction for deletion. */ - public S3Response handleRequest(S3DeleteObjectRequest request) - { - // Verify that the bucket and object exist - S3Response response = new S3Response(); - - String bucketName = request.getBucketName(); - SBucketVO sbucket = bucketDao.getByName( bucketName ); - if (sbucket == null) { - response.setResultCode(404); - response.setResultDescription("Bucket dosen't existsBucket " + bucketName + " does not exist"); - return response; - } - - - String nameKey = request.getKey(); - SObjectVO sobject = objectDao.getByNameKey( sbucket, nameKey ); - if (sobject == null) { - response.setResultCode(404); - response.setResultDescription("Not FoundNo object with key " + nameKey + " exists in bucket " + bucketName+""); - return response; - } - - - // Discover whether versioning is enabled. If so versioning requires the setting of a deletion marker. - String storedPath = null; - SObjectItemVO item = null; + public S3Response handleRequest(S3DeleteObjectRequest request) + { + // Verify that the bucket and object exist + S3Response response = new S3Response(); + + String bucketName = request.getBucketName(); + SBucketVO sbucket = bucketDao.getByName( bucketName ); + if (sbucket == null) { + response.setResultCode(404); + response.setResultDescription("Bucket dosen't existsBucket " + bucketName + " does not exist"); + return response; + } + + + String nameKey = request.getKey(); + SObjectVO sobject = objectDao.getByNameKey( sbucket, nameKey ); + if (sobject == null) { + response.setResultCode(404); + response.setResultDescription("Not FoundNo object with key " + nameKey + " exists in bucket " + bucketName+""); + return response; + } + + + // Discover whether versioning is enabled. If so versioning requires the setting of a deletion marker. + String storedPath = null; + SObjectItemVO item = null; int versioningStatus = sbucket.getVersioningStatus(); - if ( SBucket.VERSIONING_ENABLED == versioningStatus ) - { - String wantVersion = request.getVersion(); - S3PolicyContext context = new S3PolicyContext( PolicyActions.DeleteObjectVersion, bucketName ); - context.setKeyName( nameKey ); - context.setEvalParam( ConditionKeys.VersionId, wantVersion ); - verifyAccess( context, "SBucket", sbucket.getId(), SAcl.PERMISSION_WRITE ); + if ( SBucket.VERSIONING_ENABLED == versioningStatus ) + { + String wantVersion = request.getVersion(); + S3PolicyContext context = new S3PolicyContext( PolicyActions.DeleteObjectVersion, bucketName ); + context.setKeyName( nameKey ); + context.setEvalParam( ConditionKeys.VersionId, wantVersion ); + verifyAccess( context, "SBucket", sbucket.getId(), SAcl.PERMISSION_WRITE ); - if (null == wantVersion) { - // If versioning is on and no versionId is given then we just write a deletion marker - sobject.setDeletionMark( UUID.randomUUID().toString()); - objectDao.update(sobject.getId(), sobject ); - response.setResultDescription("true"+ sobject.getDeletionMark() +""); - } - else { - // Otherwise remove the deletion marker if this has been set - String deletionMarker = sobject.getDeletionMark(); - if (null != deletionMarker && wantVersion.equalsIgnoreCase( deletionMarker )) { - sobject.setDeletionMark( null ); - objectDao.update(sobject.getId(), sobject ); - response.setResultDescription("" + wantVersion +""); - response.setResultDescription("true"+ sobject.getDeletionMark() +""); - response.setResultCode(204); - return response; - } - - // If versioning is on and the versionId is given (non-null) then delete the object matching that version - if ( null == (item = sobject.getVersion( wantVersion ))) { - response.setResultCode(404); - return response; - } - else { - // Providing versionId is non-null, then just delete the one item that matches the versionId from the database - storedPath = item.getStoredPath(); - sobject.deleteItem( item.getId()); - objectDao.update(sobject.getId(), sobject ); - response.setResultDescription("" + wantVersion +""); - } - } - } - else - { // If versioning is off then we do delete the null object - S3PolicyContext context = new S3PolicyContext( PolicyActions.DeleteObject, bucketName ); - context.setKeyName( nameKey ); - verifyAccess( context, "SBucket", sbucket.getId(), SAcl.PERMISSION_WRITE ); + if (null == wantVersion) { + // If versioning is on and no versionId is given then we just write a deletion marker + sobject.setDeletionMark( UUID.randomUUID().toString()); + objectDao.update(sobject.getId(), sobject ); + response.setResultDescription("true"+ sobject.getDeletionMark() +""); + } + else { + // Otherwise remove the deletion marker if this has been set + String deletionMarker = sobject.getDeletionMark(); + if (null != deletionMarker && wantVersion.equalsIgnoreCase( deletionMarker )) { + sobject.setDeletionMark( null ); + objectDao.update(sobject.getId(), sobject ); + response.setResultDescription("" + wantVersion +""); + response.setResultDescription("true"+ sobject.getDeletionMark() +""); + response.setResultCode(204); + return response; + } - if ( null == (item = sobject.getLatestVersion( true ))) { - response.setResultCode(404); - response.setResultDescription("AccessDeniedAccess Denied"); - return response; - } - else { - // If there is no item with a null version then we are done - if (null == item.getVersion()) { - // Otherwiswe remove the entire object - // Cascade-deleting can delete related SObject/SObjectItem objects, but not SAcl and SMeta objects. - storedPath = item.getStoredPath(); - deleteMetaData( item.getId()); - deleteObjectAcls( "SObjectItem", item.getId()); - objectDao.remove(sobject.getId()); - } - } - } - - // Delete the file holding the object - if (null != storedPath) - { - OrderedPair host_storagelocation_pair = getBucketStorageHost( sbucket ); - S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter( host_storagelocation_pair.getFirst()); - bucketAdapter.deleteObject( host_storagelocation_pair.getSecond(), bucketName, storedPath ); - } - - response.setResultCode(204); - return response; + // If versioning is on and the versionId is given (non-null) then delete the object matching that version + if ( null == (item = sobject.getVersion( wantVersion ))) { + response.setResultCode(404); + return response; + } + else { + // Providing versionId is non-null, then just delete the one item that matches the versionId from the database + storedPath = item.getStoredPath(); + sobject.deleteItem( item.getId()); + objectDao.update(sobject.getId(), sobject ); + response.setResultDescription("" + wantVersion +""); + } + } + } + else + { // If versioning is off then we do delete the null object + S3PolicyContext context = new S3PolicyContext( PolicyActions.DeleteObject, bucketName ); + context.setKeyName( nameKey ); + verifyAccess( context, "SBucket", sbucket.getId(), SAcl.PERMISSION_WRITE ); + + if ( null == (item = sobject.getLatestVersion( true ))) { + response.setResultCode(404); + response.setResultDescription("AccessDeniedAccess Denied"); + return response; + } + else { + // If there is no item with a null version then we are done + if (null == item.getVersion()) { + // Otherwiswe remove the entire object + // Cascade-deleting can delete related SObject/SObjectItem objects, but not SAcl and SMeta objects. + storedPath = item.getStoredPath(); + deleteMetaData( item.getId()); + deleteObjectAcls( "SObjectItem", item.getId()); + objectDao.remove(sobject.getId()); + } + } + } + + // Delete the file holding the object + if (null != storedPath) + { + OrderedPair host_storagelocation_pair = getBucketStorageHost( sbucket ); + S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter( host_storagelocation_pair.getFirst()); + bucketAdapter.deleteObject( host_storagelocation_pair.getSecond(), bucketName, storedPath ); + } + + response.setResultCode(204); + return response; } - - private void deleteMetaData( long itemId ) { - List itemMetaData = metaDao.getByTarget( "SObjectItem", itemId ); - if (null != itemMetaData) - { - ListIterator it = itemMetaData.listIterator(); - while( it.hasNext()) { - SMetaVO oneTag = (SMetaVO)it.next(); - metaDao.remove(oneTag.getId()); - } - } - } - private void deleteObjectAcls( String target, long itemId ) { - List itemAclData = aclDao.listGrants( target, itemId ); - if (null != itemAclData) - { - ListIterator it = itemAclData.listIterator(); - while( it.hasNext()) { - SAclVO oneTag = (SAclVO)it.next(); - aclDao.remove(oneTag.getId()); - } - } - } + private void deleteMetaData( long itemId ) { + List itemMetaData = metaDao.getByTarget( "SObjectItem", itemId ); + if (null != itemMetaData) + { + ListIterator it = itemMetaData.listIterator(); + while( it.hasNext()) { + SMetaVO oneTag = it.next(); + metaDao.remove(oneTag.getId()); + } + } + } - private void deleteBucketAcls( long bucketId ) { + private void deleteObjectAcls( String target, long itemId ) { + List itemAclData = aclDao.listGrants( target, itemId ); + if (null != itemAclData) + { + ListIterator it = itemAclData.listIterator(); + while( it.hasNext()) { + SAclVO oneTag = it.next(); + aclDao.remove(oneTag.getId()); + } + } + } - List bucketAclData = aclDao.listGrants( "SBucket", bucketId ); - if (null != bucketAclData) - { - ListIterator it = bucketAclData.listIterator(); - while( it.hasNext()) { - SAclVO oneTag = (SAclVO)it.next(); - aclDao.remove(oneTag.getId()); - } - } - } - - private S3ListBucketPrefixEntry[] composeListBucketPrefixEntries(List l, String prefix, String delimiter, int maxKeys) - { - List entries = new ArrayList(); - int count = 0; - - for(SObjectVO sobject : l) - { - if(delimiter != null && !delimiter.isEmpty()) - { - String subName = StringHelper.substringInBetween(sobject.getNameKey(), prefix, delimiter); - if(subName != null) - { - S3ListBucketPrefixEntry entry = new S3ListBucketPrefixEntry(); - if ( prefix != null && prefix.length() > 0) - entry.setPrefix(prefix + delimiter + subName); - else entry.setPrefix(subName); - } - } - count++; - if(count >= maxKeys) break; - } - - if(entries.size() > 0) return entries.toArray(new S3ListBucketPrefixEntry[0]); - return null; - } - - /** - * The 'versionIdMarker' parameter only makes sense if enableVersion is true. - * versionIdMarker is the starting point to return information back. So for example if an - * object has versions 1,2,3,4,5 and the versionIdMarker is '3', then 3,4,5 will be returned - * by this function. If the versionIdMarker is null then all versions are returned. - * - * TODO - how does the versionIdMarker work when there is a deletion marker in the object? - */ - private S3ListBucketObjectEntry[] composeListBucketContentEntries(List l, String prefix, String delimiter, int maxKeys, boolean enableVersion, String versionIdMarker) - { - List entries = new ArrayList(); - SObjectItemVO latest = null; - boolean hitIdMarker = false; - int count = 0; - - for( SObjectVO sobject : l ) - { - if (delimiter != null && !delimiter.isEmpty()) - { - if (StringHelper.substringInBetween(sobject.getNameKey(), prefix, delimiter) != null) - continue; - } - - if (enableVersion) - { - hitIdMarker = (null == versionIdMarker ? true : false); + private void deleteBucketAcls( long bucketId ) { - // This supports GET REST calls with /?versions - String deletionMarker = sobject.getDeletionMark(); + List bucketAclData = aclDao.listGrants( "SBucket", bucketId ); + if (null != bucketAclData) + { + ListIterator it = bucketAclData.listIterator(); + while( it.hasNext()) { + SAclVO oneTag = it.next(); + aclDao.remove(oneTag.getId()); + } + } + } + + private S3ListBucketPrefixEntry[] composeListBucketPrefixEntries(List l, String prefix, String delimiter, int maxKeys) + { + List entries = new ArrayList(); + int count = 0; + + for(SObjectVO sobject : l) + { + if(delimiter != null && !delimiter.isEmpty()) + { + String subName = StringHelper.substringInBetween(sobject.getNameKey(), prefix, delimiter); + if(subName != null) + { + S3ListBucketPrefixEntry entry = new S3ListBucketPrefixEntry(); + if ( prefix != null && prefix.length() > 0) + entry.setPrefix(prefix + delimiter + subName); + else entry.setPrefix(subName); + } + } + count++; + if(count >= maxKeys) break; + } + + if(entries.size() > 0) return entries.toArray(new S3ListBucketPrefixEntry[0]); + return null; + } + + /** + * The 'versionIdMarker' parameter only makes sense if enableVersion is true. + * versionIdMarker is the starting point to return information back. So for example if an + * object has versions 1,2,3,4,5 and the versionIdMarker is '3', then 3,4,5 will be returned + * by this function. If the versionIdMarker is null then all versions are returned. + * + * TODO - how does the versionIdMarker work when there is a deletion marker in the object? + */ + private S3ListBucketObjectEntry[] composeListBucketContentEntries(List l, String prefix, String delimiter, int maxKeys, boolean enableVersion, String versionIdMarker) + { + List entries = new ArrayList(); + SObjectItemVO latest = null; + boolean hitIdMarker = false; + int count = 0; + + for( SObjectVO sobject : l ) + { + if (delimiter != null && !delimiter.isEmpty()) + { + if (StringHelper.substringInBetween(sobject.getNameKey(), prefix, delimiter) != null) + continue; + } + + if (enableVersion) + { + hitIdMarker = (null == versionIdMarker ? true : false); + + // This supports GET REST calls with /?versions + String deletionMarker = sobject.getDeletionMark(); if ( null != deletionMarker ) { - // TODO we should also save the timestamp when something is deleted - S3ListBucketObjectEntry entry = new S3ListBucketObjectEntry(); - entry.setKey(sobject.getNameKey()); - entry.setVersion( deletionMarker ); - entry.setIsLatest( true ); - entry.setIsDeletionMarker( true ); - entry.setLastModified( Calendar.getInstance( TimeZone.getTimeZone("GMT") )); - entry.setOwnerCanonicalId(sobject.getOwnerCanonicalId()); - entry.setOwnerDisplayName(""); - entries.add( entry ); - latest = null; + // TODO we should also save the timestamp when something is deleted + S3ListBucketObjectEntry entry = new S3ListBucketObjectEntry(); + entry.setKey(sobject.getNameKey()); + entry.setVersion( deletionMarker ); + entry.setIsLatest( true ); + entry.setIsDeletionMarker( true ); + entry.setLastModified( Calendar.getInstance( TimeZone.getTimeZone("GMT") )); + entry.setOwnerCanonicalId(sobject.getOwnerCanonicalId()); + entry.setOwnerDisplayName(""); + entries.add( entry ); + latest = null; } else latest = sobject.getLatestVersion( false ); - - Iterator it = sobject.getItems().iterator(); - while( it.hasNext()) - { - SObjectItemVO item = (SObjectItemVO)it.next(); - - if ( !hitIdMarker ) - { - if (item.getVersion().equalsIgnoreCase( versionIdMarker )) { - hitIdMarker = true; - entries.add( toListEntry( sobject, item, latest )); - } - } - else entries.add( toListEntry( sobject, item, latest )); - } - } - else - { // -> if there are multiple versions of an object then just return its last version - Iterator it = sobject.getItems().iterator(); - SObjectItemVO lastestItem = null; - int maxVersion = 0; - int version = 0; - while(it.hasNext()) - { - SObjectItemVO item = (SObjectItemVO)it.next(); - String versionStr = item.getVersion(); - - if ( null != versionStr ) - version = Integer.parseInt(item.getVersion()); - else lastestItem = item; - - // -> if the bucket has versions turned on - if (version > maxVersion) { - maxVersion = version; - lastestItem = item; - } - } - if (lastestItem != null) { - entries.add( toListEntry( sobject, lastestItem, null )); - } - } - - count++; - if(count >= maxKeys) break; - } - - if ( entries.size() > 0 ) - return entries.toArray(new S3ListBucketObjectEntry[0]); - else return null; - } - - private static S3ListBucketObjectEntry toListEntry( SObjectVO sobject, SObjectItemVO item, SObjectItemVO latest ) - { - S3ListBucketObjectEntry entry = new S3ListBucketObjectEntry(); - entry.setKey(sobject.getNameKey()); - entry.setVersion( item.getVersion()); - entry.setETag( "\"" + item.getMd5() + "\"" ); - entry.setSize(item.getStoredSize()); - entry.setStorageClass( "STANDARD" ); - entry.setLastModified(DateHelper.toCalendar(item.getLastModifiedTime())); - entry.setOwnerCanonicalId(sobject.getOwnerCanonicalId()); - entry.setOwnerDisplayName(""); - - if (null != latest && item == latest) entry.setIsLatest( true ); - return entry; - } - - private OrderedPair getBucketStorageHost(SBucketVO bucket) - { - - SHostVO shost = shostDao.findById(bucket.getShostID()); - if(shost.getHostType() == SHost.STORAGE_HOST_TYPE_LOCAL) { - return new OrderedPair(shost, shost.getExportRoot()); - } - + + Iterator it = sobject.getItems().iterator(); + while( it.hasNext()) + { + SObjectItemVO item = it.next(); + + if ( !hitIdMarker ) + { + if (item.getVersion().equalsIgnoreCase( versionIdMarker )) { + hitIdMarker = true; + entries.add( toListEntry( sobject, item, latest )); + } + } + else entries.add( toListEntry( sobject, item, latest )); + } + } + else + { // -> if there are multiple versions of an object then just return its last version + Iterator it = sobject.getItems().iterator(); + SObjectItemVO lastestItem = null; + int maxVersion = 0; + int version = 0; + while(it.hasNext()) + { + SObjectItemVO item = it.next(); + String versionStr = item.getVersion(); + + if ( null != versionStr ) + version = Integer.parseInt(item.getVersion()); + else lastestItem = item; + + // -> if the bucket has versions turned on + if (version > maxVersion) { + maxVersion = version; + lastestItem = item; + } + } + if (lastestItem != null) { + entries.add( toListEntry( sobject, lastestItem, null )); + } + } + + count++; + if(count >= maxKeys) break; + } + + if ( entries.size() > 0 ) + return entries.toArray(new S3ListBucketObjectEntry[0]); + else return null; + } + + private static S3ListBucketObjectEntry toListEntry( SObjectVO sobject, SObjectItemVO item, SObjectItemVO latest ) + { + S3ListBucketObjectEntry entry = new S3ListBucketObjectEntry(); + entry.setKey(sobject.getNameKey()); + entry.setVersion( item.getVersion()); + entry.setETag( "\"" + item.getMd5() + "\"" ); + entry.setSize(item.getStoredSize()); + entry.setStorageClass( "STANDARD" ); + entry.setLastModified(DateHelper.toCalendar(item.getLastModifiedTime())); + entry.setOwnerCanonicalId(sobject.getOwnerCanonicalId()); + entry.setOwnerDisplayName(""); + + if (null != latest && item == latest) entry.setIsLatest( true ); + return entry; + } + + private OrderedPair getBucketStorageHost(SBucketVO bucket) + { + + SHostVO shost = shostDao.findById(bucket.getShostID()); + if(shost.getHostType() == SHost.STORAGE_HOST_TYPE_LOCAL) { + return new OrderedPair(shost, shost.getExportRoot()); + } + if(shost.getHostType() == SHost.STORAGE_HOST_TYPE_CASTOR ) { return new OrderedPair(shost, shost.getExportRoot()); } - MHostMountVO mount = mountDao.getHostMount(ServiceProvider.getInstance().getManagementHostId(), shost.getId()); - if(mount != null) { - return new OrderedPair(shost, mount.getMountPath()); - } - //return null; - // need to redirect request to other node - throw new HostNotMountedException("Storage host "); // + shost.getHost() + " is not locally mounted"); - } - - /** - * Locate the folder to hold upload parts at the same mount point as the upload's final bucket - * location. Create the upload folder dynamically. - * - * @param bucketName - */ - private void createUploadFolder(String bucketName) - { - try { - allocBucketStorageHost(bucketName, ServiceProvider.getInstance().getMultipartDir()); - } - finally { - - } - } - - /** - * The overrideName is used to create a hidden storage bucket (folder) in the same location - * as the given bucketName. This can be used to create a folder for parts of a multipart - * upload for the associated bucket. - * - * @param bucketName - * @param overrideName - * @return - */ - private OrderedPair allocBucketStorageHost(String bucketName, String overrideName) - { - //SHostDao shostDao = new SHostDao(); - - MHostVO mhost = mhostDao.findById(ServiceProvider.getInstance().getManagementHostId()); - if(mhost == null) - throw new OutOfServiceException("Temporarily out of service"); - - if(mhost.getMounts().size() > 0) { - Random random = new Random(); - MHostMountVO[] mounts = (MHostMountVO[])mhost.getMounts().toArray(); - MHostMountVO mount = mounts[random.nextInt(mounts.length)]; - S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(mount.getShost()); - bucketAdapter.createContainer(mount.getMountPath(), (null != overrideName ? overrideName : bucketName)); - return new OrderedPair(mount.getShost(), mount.getMountPath()); - } - - // To make things simple, only allow one local mounted storage root TODO - Change in the future - String localStorageRoot = ServiceProvider.getInstance().getStartupProperties().getProperty("storage.root"); - if(localStorageRoot != null) { - SHostVO localSHost = shostDao.getLocalStorageHost(mhost.getId(), localStorageRoot); - if(localSHost == null) - throw new InternalErrorException("storage.root is configured but not initialized"); - - S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(localSHost); - bucketAdapter.createContainer(localSHost.getExportRoot(),(null != overrideName ? overrideName : bucketName)); - return new OrderedPair(localSHost, localStorageRoot); - } - - throw new OutOfStorageException("No storage host is available"); - } - - public S3BucketAdapter getStorageHostBucketAdapter(SHostVO shost) - { - S3BucketAdapter adapter = bucketAdapters.get(shost.getHostType()); - if(adapter == null) - throw new InternalErrorException("Bucket adapter is not installed for host type: " + shost.getHostType()); - - return adapter; - } + MHostMountVO mount = mountDao.getHostMount(ServiceProvider.getInstance().getManagementHostId(), shost.getId()); + if(mount != null) { + return new OrderedPair(shost, mount.getMountPath()); + } + //return null; + // need to redirect request to other node + throw new HostNotMountedException("Storage host "); // + shost.getHost() + " is not locally mounted"); + } - /** - * If acl is set then the cannedAccessPolicy parameter should be null and is ignored. - * The cannedAccessPolicy parameter is for REST Put requests only where a simple set of ACLs can be - * created with a single header value. Note that we do not currently support "anonymous" un-authenticated - * access in our implementation. - * - * @throws IOException - */ - @SuppressWarnings("deprecation") - public OrderedPair allocObjectItem(SBucketVO bucket, String nameKey, S3MetaDataEntry[] meta, S3AccessControlList acl, String cannedAccessPolicy) - { - SObjectItemVO item = null; - int versionSeq = 1; - int versioningStatus = bucket.getVersioningStatus(); - - //Session session = PersistContext.getSession(); - - // [A] To write into a bucket the user must have write permission to that bucket - S3PolicyContext context = new S3PolicyContext( PolicyActions.PutObject, bucket.getName()); - context.setKeyName( nameKey ); - context.setEvalParam( ConditionKeys.Acl, cannedAccessPolicy); + /** + * Locate the folder to hold upload parts at the same mount point as the upload's final bucket + * location. Create the upload folder dynamically. + * + * @param bucketName + */ + private void createUploadFolder(String bucketName) + { + try { + allocBucketStorageHost(bucketName, ServiceProvider.getInstance().getMultipartDir()); + } + finally { - verifyAccess( context, "SBucket", bucket.getId(), SAcl.PERMISSION_WRITE ); // TODO - check this validates plain POSTs - Transaction txn = Transaction.open(Transaction.AWSAPI_DB); - txn.start(); + } + } - // [B] If versioning is off them we over write a null object item - SObjectVO object = objectDao.getByNameKey(bucket, nameKey); - if ( object != null ) - { - // -> if versioning is on create new object items - if ( SBucket.VERSIONING_ENABLED == versioningStatus ) - { + /** + * The overrideName is used to create a hidden storage bucket (folder) in the same location + * as the given bucketName. This can be used to create a folder for parts of a multipart + * upload for the associated bucket. + * + * @param bucketName + * @param overrideName + * @return + */ + private OrderedPair allocBucketStorageHost(String bucketName, String overrideName) + { + //SHostDao shostDao = new SHostDao(); - versionSeq = object.getNextSequence(); - object.setNextSequence(versionSeq + 1); - objectDao.update(object.getId(), object); - - item = new SObjectItemVO(); - item.setTheObject(object); - object.getItems().add(item); - item.setsObjectID(object.getId()); - item.setVersion(String.valueOf(versionSeq)); - Date ts = DateHelper.currentGMTTime(); - item.setCreateTime(ts); - item.setLastAccessTime(ts); - item.setLastModifiedTime(ts); - item = itemDao.persist(item); - txn.commit(); - //session.save(item); - } - else - { // -> find an object item with a null version, can be null - // if bucket started out with versioning enabled and was then suspended - item = itemDao.getByObjectIdNullVersion( object.getId()); - if (item == null) - { - item = new SObjectItemVO(); - item.setTheObject(object); - item.setsObjectID(object.getId()); - object.getItems().add(item); - Date ts = DateHelper.currentGMTTime(); - item.setCreateTime(ts); - item.setLastAccessTime(ts); - item.setLastModifiedTime(ts); - item = itemDao.persist(item); - txn.commit(); - } - } - } - else - { - Transaction txn1 = Transaction.open(Transaction.AWSAPI_DB); - txn1.start(); - // -> there is no object nor an object item - object = new SObjectVO(); - object.setBucket(bucket); - object.setNameKey(nameKey); - object.setNextSequence(2); - object.setBucketID(bucket.getId()); - object.setCreateTime(DateHelper.currentGMTTime()); - object.setOwnerCanonicalId(UserContext.current().getCanonicalUserId()); - object = objectDao.persist(object); - item = new SObjectItemVO(); - item.setTheObject(object); - item.setsObjectID(object.getId()); - object.getItems().add(item); - if (SBucket.VERSIONING_ENABLED == versioningStatus) item.setVersion(String.valueOf(versionSeq)); - Date ts = DateHelper.currentGMTTime(); - item.setCreateTime(ts); - item.setLastAccessTime(ts); - item.setLastModifiedTime(ts); - item = itemDao.persist(item); - txn.commit(); - txn.close(); - - } - - - // [C] We will use the item DB id as the file name, MD5/contentLength will be stored later - String suffix = null; - int dotPos = nameKey.lastIndexOf('.'); - if (dotPos >= 0) suffix = nameKey.substring(dotPos); - if ( suffix != null ) - item.setStoredPath(String.valueOf(item.getId()) + suffix); - else item.setStoredPath(String.valueOf(item.getId())); - - metaDao.save("SObjectItem", item.getId(), meta); - - - // [D] Are we setting an ACL along with the object - // -> the ACL is ALWAYS set on a particular instance of the object (i.e., a version) - if ( null != cannedAccessPolicy ) - { - setCannedAccessControls( cannedAccessPolicy, "SObjectItem", item.getId(), bucket ); - } - else if (null == acl || 0 == acl.size()) - { - // -> this is termed the "private" or default ACL, "Owner gets FULL_CONTROL" - setSingleAcl( "SObjectItem", item.getId(), SAcl.PERMISSION_FULL ); - } - else if (null != acl) { - aclDao.save( "SObjectItem", item.getId(), acl ); - } - - itemDao.update(item.getId(), item); - txn.close(); - return new OrderedPair(object, item); - } - - - /** - * Access controls that are specified via the "x-amz-acl:" headers in REST requests. - * Note that canned policies can be set when the object's contents are set - */ - public void setCannedAccessControls( String cannedAccessPolicy, String target, long objectId, SBucketVO bucket ) - { - // Find the permission and symbol for the principal corresponding to the requested cannedAccessPolicy - Triple permission_permission_symbol_triple = - SAclVO.getCannedAccessControls(cannedAccessPolicy, target, bucket.getOwnerCanonicalId()); - if ( null == permission_permission_symbol_triple.getThird() ) - setSingleAcl(target, objectId, permission_permission_symbol_triple.getFirst()); - else - { setDefaultAcls( target, - objectId, - permission_permission_symbol_triple.getFirst(), // permission according to ownership of object - permission_permission_symbol_triple.getSecond(), // permission according to ownership of bucket - permission_permission_symbol_triple.getThird() ); // "symbol" to indicate principal or otherwise name of owner - - } - } + MHostVO mhost = mhostDao.findById(ServiceProvider.getInstance().getManagementHostId()); + if(mhost == null) + throw new OutOfServiceException("Temporarily out of service"); - - private void setSingleAcl( String target, long targetId, int permission ) - { + if(mhost.getMounts().size() > 0) { + Random random = new Random(); + MHostMountVO[] mounts = (MHostMountVO[])mhost.getMounts().toArray(); + MHostMountVO mount = mounts[random.nextInt(mounts.length)]; + S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(mount.getShost()); + bucketAdapter.createContainer(mount.getMountPath(), (null != overrideName ? overrideName : bucketName)); + return new OrderedPair(mount.getShost(), mount.getMountPath()); + } + + // To make things simple, only allow one local mounted storage root TODO - Change in the future + String localStorageRoot = ServiceProvider.getInstance().getStartupProperties().getProperty("storage.root"); + if(localStorageRoot != null) { + SHostVO localSHost = shostDao.getLocalStorageHost(mhost.getId(), localStorageRoot); + if(localSHost == null) + throw new InternalErrorException("storage.root is configured but not initialized"); + + S3BucketAdapter bucketAdapter = getStorageHostBucketAdapter(localSHost); + bucketAdapter.createContainer(localSHost.getExportRoot(),(null != overrideName ? overrideName : bucketName)); + return new OrderedPair(localSHost, localStorageRoot); + } + + throw new OutOfStorageException("No storage host is available"); + } + + public S3BucketAdapter getStorageHostBucketAdapter(SHostVO shost) + { + S3BucketAdapter adapter = bucketAdapters.get(shost.getHostType()); + if(adapter == null) + throw new InternalErrorException("Bucket adapter is not installed for host type: " + shost.getHostType()); + + return adapter; + } + + /** + * If acl is set then the cannedAccessPolicy parameter should be null and is ignored. + * The cannedAccessPolicy parameter is for REST Put requests only where a simple set of ACLs can be + * created with a single header value. Note that we do not currently support "anonymous" un-authenticated + * access in our implementation. + * + * @throws IOException + */ + @SuppressWarnings("deprecation") + public OrderedPair allocObjectItem(SBucketVO bucket, String nameKey, S3MetaDataEntry[] meta, S3AccessControlList acl, String cannedAccessPolicy) + { + SObjectItemVO item = null; + int versionSeq = 1; + int versioningStatus = bucket.getVersioningStatus(); + + //Session session = PersistContext.getSession(); + + // [A] To write into a bucket the user must have write permission to that bucket + S3PolicyContext context = new S3PolicyContext( PolicyActions.PutObject, bucket.getName()); + context.setKeyName( nameKey ); + context.setEvalParam( ConditionKeys.Acl, cannedAccessPolicy); + + verifyAccess( context, "SBucket", bucket.getId(), SAcl.PERMISSION_WRITE ); // TODO - check this validates plain POSTs + Transaction txn = Transaction.open(Transaction.AWSAPI_DB); + txn.start(); + + // [B] If versioning is off them we over write a null object item + SObjectVO object = objectDao.getByNameKey(bucket, nameKey); + if ( object != null ) + { + // -> if versioning is on create new object items + if ( SBucket.VERSIONING_ENABLED == versioningStatus ) + { + + versionSeq = object.getNextSequence(); + object.setNextSequence(versionSeq + 1); + objectDao.update(object.getId(), object); + + item = new SObjectItemVO(); + item.setTheObject(object); + object.getItems().add(item); + item.setsObjectID(object.getId()); + item.setVersion(String.valueOf(versionSeq)); + Date ts = DateHelper.currentGMTTime(); + item.setCreateTime(ts); + item.setLastAccessTime(ts); + item.setLastModifiedTime(ts); + item = itemDao.persist(item); + txn.commit(); + //session.save(item); + } + else + { // -> find an object item with a null version, can be null + // if bucket started out with versioning enabled and was then suspended + item = itemDao.getByObjectIdNullVersion( object.getId()); + if (item == null) + { + item = new SObjectItemVO(); + item.setTheObject(object); + item.setsObjectID(object.getId()); + object.getItems().add(item); + Date ts = DateHelper.currentGMTTime(); + item.setCreateTime(ts); + item.setLastAccessTime(ts); + item.setLastModifiedTime(ts); + item = itemDao.persist(item); + txn.commit(); + } + } + } + else + { + Transaction txn1 = Transaction.open(Transaction.AWSAPI_DB); + txn1.start(); + // -> there is no object nor an object item + object = new SObjectVO(); + object.setBucket(bucket); + object.setNameKey(nameKey); + object.setNextSequence(2); + object.setBucketID(bucket.getId()); + object.setCreateTime(DateHelper.currentGMTTime()); + object.setOwnerCanonicalId(UserContext.current().getCanonicalUserId()); + object = objectDao.persist(object); + item = new SObjectItemVO(); + item.setTheObject(object); + item.setsObjectID(object.getId()); + object.getItems().add(item); + if (SBucket.VERSIONING_ENABLED == versioningStatus) item.setVersion(String.valueOf(versionSeq)); + Date ts = DateHelper.currentGMTTime(); + item.setCreateTime(ts); + item.setLastAccessTime(ts); + item.setLastModifiedTime(ts); + item = itemDao.persist(item); + txn.commit(); + txn.close(); + + } + + + // [C] We will use the item DB id as the file name, MD5/contentLength will be stored later + String suffix = null; + int dotPos = nameKey.lastIndexOf('.'); + if (dotPos >= 0) suffix = nameKey.substring(dotPos); + if ( suffix != null ) + item.setStoredPath(String.valueOf(item.getId()) + suffix); + else item.setStoredPath(String.valueOf(item.getId())); + + metaDao.save("SObjectItem", item.getId(), meta); + + + // [D] Are we setting an ACL along with the object + // -> the ACL is ALWAYS set on a particular instance of the object (i.e., a version) + if ( null != cannedAccessPolicy ) + { + setCannedAccessControls( cannedAccessPolicy, "SObjectItem", item.getId(), bucket ); + } + else if (null == acl || 0 == acl.size()) + { + // -> this is termed the "private" or default ACL, "Owner gets FULL_CONTROL" + setSingleAcl( "SObjectItem", item.getId(), SAcl.PERMISSION_FULL ); + } + else if (null != acl) { + aclDao.save( "SObjectItem", item.getId(), acl ); + } + + itemDao.update(item.getId(), item); + txn.close(); + return new OrderedPair(object, item); + } + + + /** + * Access controls that are specified via the "x-amz-acl:" headers in REST requests. + * Note that canned policies can be set when the object's contents are set + */ + public void setCannedAccessControls( String cannedAccessPolicy, String target, long objectId, SBucketVO bucket ) + { + // Find the permission and symbol for the principal corresponding to the requested cannedAccessPolicy + Triple permission_permission_symbol_triple = + SAclVO.getCannedAccessControls(cannedAccessPolicy, target, bucket.getOwnerCanonicalId()); + if ( null == permission_permission_symbol_triple.getThird() ) + setSingleAcl(target, objectId, permission_permission_symbol_triple.getFirst()); + else + { setDefaultAcls( target, + objectId, + permission_permission_symbol_triple.getFirst(), // permission according to ownership of object + permission_permission_symbol_triple.getSecond(), // permission according to ownership of bucket + permission_permission_symbol_triple.getThird() ); // "symbol" to indicate principal or otherwise name of owner + + } + } + + + private void setSingleAcl( String target, long targetId, int permission ) + { S3AccessControlList defaultAcl = new S3AccessControlList(); - - // -> if an annoymous request, then do not rewrite the ACL - String userId = UserContext.current().getCanonicalUserId(); + + // -> if an annoymous request, then do not rewrite the ACL + String userId = UserContext.current().getCanonicalUserId(); if (0 < userId.length()) { S3Grant defaultGrant = new S3Grant(); @@ -1647,28 +1638,28 @@ public class S3Engine { defaultAcl.addGrant( defaultGrant ); aclDao.save( target, targetId, defaultAcl ); } - } - + } - /** - * The Cloud Stack API Access key is used for for the Canonical User Id everywhere (buckets and objects). - * - * @param owner - this can be the Cloud Access Key for a bucket owner or one of the - * following special symbols: - * (a) '*' - any principal authenticated user (i.e., any user with a registered Cloud Access Key) - * (b) 'A' - any anonymous principal (i.e., S3 request without an Authorization header) - */ - private void setDefaultAcls( String target, long objectId, int permission1, int permission2, String owner ) - { - S3AccessControlList defaultAcl = new S3AccessControlList(); - - // -> object owner + + /** + * The Cloud Stack API Access key is used for for the Canonical User Id everywhere (buckets and objects). + * + * @param owner - this can be the Cloud Access Key for a bucket owner or one of the + * following special symbols: + * (a) '*' - any principal authenticated user (i.e., any user with a registered Cloud Access Key) + * (b) 'A' - any anonymous principal (i.e., S3 request without an Authorization header) + */ + private void setDefaultAcls( String target, long objectId, int permission1, int permission2, String owner ) + { + S3AccessControlList defaultAcl = new S3AccessControlList(); + + // -> object owner S3Grant defaultGrant = new S3Grant(); defaultGrant.setGrantee(SAcl.GRANTEE_USER); defaultGrant.setCanonicalUserID( UserContext.current().getCanonicalUserId()); defaultGrant.setPermission( permission1 ); defaultAcl.addGrant( defaultGrant ); - + // -> bucket owner defaultGrant = new S3Grant(); defaultGrant.setGrantee(SAcl.GRANTEE_USER); @@ -1676,238 +1667,238 @@ public class S3Engine { defaultGrant.setPermission( permission2 ); defaultAcl.addGrant( defaultGrant ); aclDao.save( target, objectId, defaultAcl ); - } + } - public static PolicyAccess verifyPolicy( S3PolicyContext context ) - { - S3BucketPolicy policy = null; - - // Ordinarily a REST request will pass in an S3PolicyContext for a given bucket by this stage. The HttpServletRequest object - // should be held in the UserContext ready for extraction of the S3BucketPolicy. - // If there is an error in obtaining the request object or in loading the policy then log the failure and return a S3PolicyContext - // which indicates DEFAULT_DENY. Where there is no failure, the policy returned should be specific to the Canonical User ID of the requester. - - try { - // -> in SOAP the HttpServletRequest object is hidden and not passed around - if (null != context) { - context.setHttp( UserContext.current().getHttp()); - policy = loadPolicy( context ); - } - - if ( null != policy ) - return policy.eval(context, UserContext.current().getCanonicalUserId()); - else return PolicyAccess.DEFAULT_DENY; - } - catch( Exception e ) { + public static PolicyAccess verifyPolicy( S3PolicyContext context ) + { + S3BucketPolicy policy = null; + + // Ordinarily a REST request will pass in an S3PolicyContext for a given bucket by this stage. The HttpServletRequest object + // should be held in the UserContext ready for extraction of the S3BucketPolicy. + // If there is an error in obtaining the request object or in loading the policy then log the failure and return a S3PolicyContext + // which indicates DEFAULT_DENY. Where there is no failure, the policy returned should be specific to the Canonical User ID of the requester. + + try { + // -> in SOAP the HttpServletRequest object is hidden and not passed around + if (null != context) { + context.setHttp( UserContext.current().getHttp()); + policy = loadPolicy( context ); + } + + if ( null != policy ) + return policy.eval(context, UserContext.current().getCanonicalUserId()); + else return PolicyAccess.DEFAULT_DENY; + } + catch( Exception e ) { logger.error("verifyAccess - loadPolicy failed, bucket: " + context.getBucketName() + " policy ignored", e); - return PolicyAccess.DEFAULT_DENY; - } - } - - /** - * To determine access to a bucket or an object in a bucket evaluate first a define - * bucket policy and then any defined ACLs. - * - * @param context - all data needed for bucket policies - * @param target - used for ACL evaluation, object identifier - * @param targetId - used for ACL evaluation - * @param requestedPermission - ACL type access requested - * - * @throws ParseException, SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException - */ - public static void verifyAccess( S3PolicyContext context, String target, long targetId, int requestedPermission ) - { - switch( verifyPolicy( context ) ) { + return PolicyAccess.DEFAULT_DENY; + } + } + + /** + * To determine access to a bucket or an object in a bucket evaluate first a define + * bucket policy and then any defined ACLs. + * + * @param context - all data needed for bucket policies + * @param target - used for ACL evaluation, object identifier + * @param targetId - used for ACL evaluation + * @param requestedPermission - ACL type access requested + * + * @throws ParseException, SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException + */ + public static void verifyAccess( S3PolicyContext context, String target, long targetId, int requestedPermission ) + { + switch( verifyPolicy( context ) ) { case ALLOW: // overrides ACLs (?) - return; + return; case DENY: - throw new PermissionDeniedException( "Access Denied - bucket policy DENY result" ); - + throw new PermissionDeniedException( "Access Denied - bucket policy DENY result" ); + case DEFAULT_DENY: default: - accessAllowed( target, targetId, requestedPermission ); - break; + accessAllowed( target, targetId, requestedPermission ); + break; } - } - - /** - * This method verifies that the accessing client has the requested - * permission on the object/bucket/Acl represented by the tuple: - * - * For cases where an ACL is meant for any authenticated user we place a "*" for the - * Canonical User Id. N.B. - "*" is not a legal Cloud (Bridge) Access key. - * - * For cases where an ACL is meant for any anonymous user (or 'AllUsers') we place a "A" for the - * Canonical User Id. N.B. - "A" is not a legal Cloud (Bridge) Access key. - */ - public static void accessAllowed( String target, long targetId, int requestedPermission ) - { - if (SAcl.PERMISSION_PASS == requestedPermission) return; - - // If an annoymous request, then canonicalUserId is an empty string - String userId = UserContext.current().getCanonicalUserId(); + } + + /** + * This method verifies that the accessing client has the requested + * permission on the object/bucket/Acl represented by the tuple: + * + * For cases where an ACL is meant for any authenticated user we place a "*" for the + * Canonical User Id. N.B. - "*" is not a legal Cloud (Bridge) Access key. + * + * For cases where an ACL is meant for any anonymous user (or 'AllUsers') we place a "A" for the + * Canonical User Id. N.B. - "A" is not a legal Cloud (Bridge) Access key. + */ + public static void accessAllowed( String target, long targetId, int requestedPermission ) + { + if (SAcl.PERMISSION_PASS == requestedPermission) return; + + // If an annoymous request, then canonicalUserId is an empty string + String userId = UserContext.current().getCanonicalUserId(); if ( 0 == userId.length()) { - // Is an anonymous principal ACL set for this ? - if (hasPermission( saclDao.listGrants( target, targetId, "A" ), requestedPermission )) return; + // Is an anonymous principal ACL set for this ? + if (hasPermission( saclDao.listGrants( target, targetId, "A" ), requestedPermission )) return; } else { - if (hasPermission( saclDao.listGrants( target, targetId, userId ), requestedPermission )) return; - // Or alternatively is there is any principal authenticated ACL set for this ? - if (hasPermission( saclDao.listGrants( target, targetId, "*" ), requestedPermission )) return; + if (hasPermission( saclDao.listGrants( target, targetId, userId ), requestedPermission )) return; + // Or alternatively is there is any principal authenticated ACL set for this ? + if (hasPermission( saclDao.listGrants( target, targetId, "*" ), requestedPermission )) return; } // No privileges implies that no access is allowed in the case of an anonymous user throw new PermissionDeniedException( "Access Denied - ACLs do not give user the required permission" ); - } - - /** - * This method assumes that the bucket has been tested to make sure it exists before - * it is called. - * - * @param context - * @return S3BucketPolicy - * @throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException, ParseException - */ - public static S3BucketPolicy loadPolicy( S3PolicyContext context ) - throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException, ParseException - { - OrderedPair result = ServiceProvider.getInstance().getBucketPolicy( context.getBucketName()); - S3BucketPolicy policy = result.getFirst(); - if ( null == policy ) - { - // -> do we have to load it from the database (any other value means there is no policy)? - if (-1 == result.getSecond().intValue()) - { - BucketPolicyVO policyvo = bPolicy.getByName(context.getBucketName()); - String policyInJson = null; - if (null != policyvo) - policyInJson = policyvo.getPolicy(); - - // -> place in cache that no policy exists in the database - if (null == policyInJson) { - ServiceProvider.getInstance().setBucketPolicy(context.getBucketName(), null); - return null; - } - - PolicyParser parser = new PolicyParser(); - policy = parser.parse( policyInJson, context.getBucketName()); - if (null != policy) - ServiceProvider.getInstance().setBucketPolicy(context.getBucketName(), policy); - } - } - return policy; - } - - public static void verifyBucketName( String bucketName, boolean useDNSGuidelines ) throws InvalidBucketName - { - // [A] To comply with Amazon S3 basic requirements, bucket names must meet the following conditions - // -> must be between 3 and 255 characters long - int size = bucketName.length(); - if (3 > size || size > 255) - throw new InvalidBucketName( bucketName + " is not between 3 and 255 characters long" ); - - // -> must start with a number or letter - if (!Character.isLetterOrDigit( bucketName.charAt( 0 ))) - throw new InvalidBucketName( bucketName + " does not start with a number or letter" ); - - // -> can contain lowercase letters, numbers, periods (.), underscores (_), and dashes (-) - // -> the bucket name can also contain uppercase letters but it is not recommended - for( int i=0; i < bucketName.length(); i++ ) - { - char next = bucketName.charAt(i); - if (Character.isLetter( next )) continue; - else if (Character.isDigit( next )) continue; - else if ('.' == next) continue; - else if ('_' == next) continue; - else if ('-' == next) continue; - else throw new InvalidBucketName( bucketName + " contains the invalid character: " + next ); - } - - // -> must not be formatted as an IP address (e.g., 192.168.5.4) - String[] parts = bucketName.split( "\\." ); - if (4 == parts.length) - { - try { - int first = Integer.parseInt( parts[0] ); - int second = Integer.parseInt( parts[1] ); - int third = Integer.parseInt( parts[2] ); - int fourth = Integer.parseInt( parts[3] ); - throw new InvalidBucketName( bucketName + " is formatted as an IP address" ); - } - catch( NumberFormatException e ) - {throw new InvalidBucketName( bucketName);} - } - - - // [B] To conform with DNS requirements, Amazon recommends following these additional guidelines when creating buckets - // -> bucket names should be between 3 and 63 characters long - if (useDNSGuidelines) - { - // -> bucket names should be between 3 and 63 characters long - if (3 > size || size > 63) - throw new InvalidBucketName( "DNS requiremens, bucket name: " + bucketName + " is not between 3 and 63 characters long" ); + } - // -> bucket names should not contain underscores (_) - int pos = bucketName.indexOf( '_' ); - if (-1 != pos) - throw new InvalidBucketName( "DNS requiremens, bucket name: " + bucketName + " should not contain underscores" ); - - // -> bucket names should not end with a dash - if (bucketName.endsWith( "-" )) - throw new InvalidBucketName( "DNS requiremens, bucket name: " + bucketName + " should not end with a dash" ); - - // -> bucket names cannot contain two, adjacent periods - pos = bucketName.indexOf( ".." ); - if (-1 != pos) - throw new InvalidBucketName( "DNS requiremens, bucket name: " + bucketName + " should not contain \"..\"" ); - - // -> bucket names cannot contain dashes next to periods (e.g., "my-.bucket.com" and "my.-bucket" are invalid) - if (-1 != bucketName.indexOf( "-." ) || -1 != bucketName.indexOf( ".-" )) - throw new InvalidBucketName( "DNS requiremens, bucket name: " + bucketName + " should not contain \".-\" or \"-.\"" ); - } - } - - private static boolean hasPermission( List privileges, int requestedPermission ) - { + /** + * This method assumes that the bucket has been tested to make sure it exists before + * it is called. + * + * @param context + * @return S3BucketPolicy + * @throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException, ParseException + */ + public static S3BucketPolicy loadPolicy( S3PolicyContext context ) + throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException, ParseException + { + OrderedPair result = ServiceProvider.getInstance().getBucketPolicy( context.getBucketName()); + S3BucketPolicy policy = result.getFirst(); + if ( null == policy ) + { + // -> do we have to load it from the database (any other value means there is no policy)? + if (-1 == result.getSecond().intValue()) + { + BucketPolicyVO policyvo = bPolicy.getByName(context.getBucketName()); + String policyInJson = null; + if (null != policyvo) + policyInJson = policyvo.getPolicy(); + + // -> place in cache that no policy exists in the database + if (null == policyInJson) { + ServiceProvider.getInstance().setBucketPolicy(context.getBucketName(), null); + return null; + } + + PolicyParser parser = new PolicyParser(); + policy = parser.parse( policyInJson, context.getBucketName()); + if (null != policy) + ServiceProvider.getInstance().setBucketPolicy(context.getBucketName(), policy); + } + } + return policy; + } + + public static void verifyBucketName( String bucketName, boolean useDNSGuidelines ) throws InvalidBucketName + { + // [A] To comply with Amazon S3 basic requirements, bucket names must meet the following conditions + // -> must be between 3 and 255 characters long + int size = bucketName.length(); + if (3 > size || size > 255) + throw new InvalidBucketName( bucketName + " is not between 3 and 255 characters long" ); + + // -> must start with a number or letter + if (!Character.isLetterOrDigit( bucketName.charAt( 0 ))) + throw new InvalidBucketName( bucketName + " does not start with a number or letter" ); + + // -> can contain lowercase letters, numbers, periods (.), underscores (_), and dashes (-) + // -> the bucket name can also contain uppercase letters but it is not recommended + for( int i=0; i < bucketName.length(); i++ ) + { + char next = bucketName.charAt(i); + if (Character.isLetter( next )) continue; + else if (Character.isDigit( next )) continue; + else if ('.' == next) continue; + else if ('_' == next) continue; + else if ('-' == next) continue; + else throw new InvalidBucketName( bucketName + " contains the invalid character: " + next ); + } + + // -> must not be formatted as an IP address (e.g., 192.168.5.4) + String[] parts = bucketName.split( "\\." ); + if (4 == parts.length) + { + try { + int first = Integer.parseInt( parts[0] ); + int second = Integer.parseInt( parts[1] ); + int third = Integer.parseInt( parts[2] ); + int fourth = Integer.parseInt( parts[3] ); + throw new InvalidBucketName( bucketName + " is formatted as an IP address" ); + } + catch( NumberFormatException e ) + {throw new InvalidBucketName( bucketName);} + } + + + // [B] To conform with DNS requirements, Amazon recommends following these additional guidelines when creating buckets + // -> bucket names should be between 3 and 63 characters long + if (useDNSGuidelines) + { + // -> bucket names should be between 3 and 63 characters long + if (3 > size || size > 63) + throw new InvalidBucketName( "DNS requiremens, bucket name: " + bucketName + " is not between 3 and 63 characters long" ); + + // -> bucket names should not contain underscores (_) + int pos = bucketName.indexOf( '_' ); + if (-1 != pos) + throw new InvalidBucketName( "DNS requiremens, bucket name: " + bucketName + " should not contain underscores" ); + + // -> bucket names should not end with a dash + if (bucketName.endsWith( "-" )) + throw new InvalidBucketName( "DNS requiremens, bucket name: " + bucketName + " should not end with a dash" ); + + // -> bucket names cannot contain two, adjacent periods + pos = bucketName.indexOf( ".." ); + if (-1 != pos) + throw new InvalidBucketName( "DNS requiremens, bucket name: " + bucketName + " should not contain \"..\"" ); + + // -> bucket names cannot contain dashes next to periods (e.g., "my-.bucket.com" and "my.-bucket" are invalid) + if (-1 != bucketName.indexOf( "-." ) || -1 != bucketName.indexOf( ".-" )) + throw new InvalidBucketName( "DNS requiremens, bucket name: " + bucketName + " should not contain \".-\" or \"-.\"" ); + } + } + + private static boolean hasPermission( List privileges, int requestedPermission ) + { ListIterator it = privileges.listIterator(); while( it.hasNext()) { - // True providing the requested permission is contained in one or the granted rights for this user. False otherwise. - SAclVO rights = (SAclVO)it.next(); - int permission = rights.getPermission(); - if (requestedPermission == (permission & requestedPermission)) return true; + // True providing the requested permission is contained in one or the granted rights for this user. False otherwise. + SAclVO rights = it.next(); + int permission = rights.getPermission(); + if (requestedPermission == (permission & requestedPermission)) return true; } return false; - } - - /** - * ifRange is true and ifUnmodifiedSince or IfMatch fails then we return the entire object (indicated by - * returning a -1 as the function result. - * - * @param ifCond - conditional get defined by these tests - * @param lastModified - value used on ifModifiedSince or ifUnmodifiedSince - * @param ETag - value used on ifMatch and ifNoneMatch - * @param ifRange - using an if-Range HTTP functionality - * @return -1 means return the entire object with an HTTP 200 (not a subrange) - */ - private int conditionPassed( S3ConditionalHeaders ifCond, Date lastModified, String ETag, boolean ifRange ) - { - if (null == ifCond) return 200; - - if (0 > ifCond.ifModifiedSince( lastModified )) - return 304; - - if (0 > ifCond.ifUnmodifiedSince( lastModified )) - return (ifRange ? -1 : 412); - - if (0 > ifCond.ifMatchEtag( ETag )) - return (ifRange ? -1 : 412); - - if (0 > ifCond.ifNoneMatchEtag( ETag )) - return 412; - - return 200; - } + } + + /** + * ifRange is true and ifUnmodifiedSince or IfMatch fails then we return the entire object (indicated by + * returning a -1 as the function result. + * + * @param ifCond - conditional get defined by these tests + * @param lastModified - value used on ifModifiedSince or ifUnmodifiedSince + * @param ETag - value used on ifMatch and ifNoneMatch + * @param ifRange - using an if-Range HTTP functionality + * @return -1 means return the entire object with an HTTP 200 (not a subrange) + */ + private int conditionPassed( S3ConditionalHeaders ifCond, Date lastModified, String ETag, boolean ifRange ) + { + if (null == ifCond) return 200; + + if (0 > ifCond.ifModifiedSince( lastModified )) + return 304; + + if (0 > ifCond.ifUnmodifiedSince( lastModified )) + return (ifRange ? -1 : 412); + + if (0 > ifCond.ifMatchEtag( ETag )) + return (ifRange ? -1 : 412); + + if (0 > ifCond.ifNoneMatchEtag( ETag )) + return 412; + + return 200; + } } diff --git a/core/src/com/cloud/storage/resource/CifsSecondaryStorageResource.java b/core/src/com/cloud/storage/resource/CifsSecondaryStorageResource.java index c606fca1fbf..0df2a8466f2 100755 --- a/core/src/com/cloud/storage/resource/CifsSecondaryStorageResource.java +++ b/core/src/com/cloud/storage/resource/CifsSecondaryStorageResource.java @@ -40,8 +40,8 @@ import com.cloud.agent.api.PingStorageCommand; import com.cloud.agent.api.ReadyAnswer; import com.cloud.agent.api.ReadyCommand; import com.cloud.agent.api.SecStorageFirewallCfgCommand; -import com.cloud.agent.api.SecStorageSetupCommand; import com.cloud.agent.api.SecStorageFirewallCfgCommand.PortConfig; +import com.cloud.agent.api.SecStorageSetupCommand; import com.cloud.agent.api.SecStorageVMSetupCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupStorageCommand; @@ -54,7 +54,6 @@ import com.cloud.agent.api.storage.UploadCommand; import com.cloud.agent.api.storage.ssCommand; import com.cloud.host.Host; import com.cloud.host.Host.Type; -import com.cloud.resource.ServerResource; import com.cloud.resource.ServerResourceBase; import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; @@ -65,7 +64,7 @@ import com.cloud.storage.template.TemplateInfo; import com.cloud.storage.template.UploadManager; import com.cloud.storage.template.UploadManagerImpl; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.utils.net.NfsUtils; @@ -81,10 +80,10 @@ import com.cloud.utils.script.Script; public class CifsSecondaryStorageResource extends ServerResourceBase implements SecondaryStorageResource { private static final Logger s_logger = Logger.getLogger(CifsSecondaryStorageResource.class); int _timeout; - + String _instance; String _parent; - + String _dc; String _pod; String _guid; @@ -94,27 +93,27 @@ public class CifsSecondaryStorageResource extends ServerResourceBase implements StorageLayer _storage; boolean _inSystemVM = false; boolean _sslCopy = false; - + Random _rand = new Random(System.currentTimeMillis()); - + DownloadManager _dlMgr; UploadManager _upldMgr; - private String _configSslScr; - private String _configAuthScr; - private String _configIpFirewallScr; - private String _publicIp; - private String _hostname; - private String _localgw; - private String _eth1mask; - private String _eth1ip; - + private String _configSslScr; + private String _configAuthScr; + private String _configIpFirewallScr; + private String _publicIp; + private String _hostname; + private String _localgw; + private String _eth1mask; + private String _eth1ip; + @Override public void disconnected() { if (_parent != null && !_inSystemVM) { Script script = new Script(!_inSystemVM, "umount", _timeout, s_logger); script.add(_parent); script.execute(); - + File file = new File(_parent); file.delete(); } @@ -133,104 +132,104 @@ public class CifsSecondaryStorageResource extends ServerResourceBase implements } else if(cmd instanceof DeleteEntityDownloadURLCommand){ return _upldMgr.handleDeleteEntityDownloadURLCommand((DeleteEntityDownloadURLCommand)cmd); } else if (cmd instanceof GetStorageStatsCommand) { - return execute((GetStorageStatsCommand)cmd); + return execute((GetStorageStatsCommand)cmd); } else if (cmd instanceof CheckHealthCommand) { return new CheckHealthAnswer((CheckHealthCommand)cmd, true); } else if (cmd instanceof DeleteTemplateCommand) { - return execute((DeleteTemplateCommand) cmd); + return execute((DeleteTemplateCommand) cmd); } else if (cmd instanceof ReadyCommand) { return new ReadyAnswer((ReadyCommand)cmd); } else if (cmd instanceof SecStorageFirewallCfgCommand){ - return execute((SecStorageFirewallCfgCommand)cmd); + return execute((SecStorageFirewallCfgCommand)cmd); } else if (cmd instanceof SecStorageVMSetupCommand){ - return execute((SecStorageVMSetupCommand)cmd); + return execute((SecStorageVMSetupCommand)cmd); } else if (cmd instanceof SecStorageSetupCommand){ return new Answer(cmd, true, "success"); } else { return Answer.createUnsupportedCommandAnswer(cmd); } } - + private Answer execute(SecStorageVMSetupCommand cmd) { - if (!_inSystemVM){ - return new Answer(cmd, true, null); - } - boolean success = true; - StringBuilder result = new StringBuilder(); - for (String cidr: cmd.getAllowedInternalSites()) { - String tmpresult = allowOutgoingOnPrivate(cidr); - if (tmpresult != null) { - result.append(", ").append(tmpresult); - success = false; - } - } - if (success) { - if (cmd.getCopyPassword() != null && cmd.getCopyUserName() != null) { - String tmpresult = configureAuth(cmd.getCopyUserName(), cmd.getCopyPassword()); - if (tmpresult != null) { - result.append("Failed to configure auth for copy ").append(tmpresult); - success = false; - } - } - } - return new Answer(cmd, success, result.toString()); + if (!_inSystemVM){ + return new Answer(cmd, true, null); + } + boolean success = true; + StringBuilder result = new StringBuilder(); + for (String cidr: cmd.getAllowedInternalSites()) { + String tmpresult = allowOutgoingOnPrivate(cidr); + if (tmpresult != null) { + result.append(", ").append(tmpresult); + success = false; + } + } + if (success) { + if (cmd.getCopyPassword() != null && cmd.getCopyUserName() != null) { + String tmpresult = configureAuth(cmd.getCopyUserName(), cmd.getCopyPassword()); + if (tmpresult != null) { + result.append("Failed to configure auth for copy ").append(tmpresult); + success = false; + } + } + } + return new Answer(cmd, success, result.toString()); + + } - } - private String allowOutgoingOnPrivate(String destCidr) { - - Script command = new Script("/bin/bash", s_logger); - String intf = "eth1"; - command.add("-c"); - command.add("iptables -I OUTPUT -o " + intf + " -d " + destCidr + " -p tcp -m state --state NEW -m tcp -j ACCEPT"); - String result = command.execute(); - if (result != null) { - s_logger.warn("Error in allowing outgoing to " + destCidr + ", err=" + result ); - return "Error in allowing outgoing to " + destCidr + ", err=" + result; - } - addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, destCidr); - return null; - } - - + Script command = new Script("/bin/bash", s_logger); + String intf = "eth1"; + command.add("-c"); + command.add("iptables -I OUTPUT -o " + intf + " -d " + destCidr + " -p tcp -m state --state NEW -m tcp -j ACCEPT"); - private Answer execute(SecStorageFirewallCfgCommand cmd) { - if (!_inSystemVM){ - return new Answer(cmd, true, null); - } + String result = command.execute(); + if (result != null) { + s_logger.warn("Error in allowing outgoing to " + destCidr + ", err=" + result ); + return "Error in allowing outgoing to " + destCidr + ", err=" + result; + } + addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, destCidr); + return null; + } - List ipList = new ArrayList(); - - for (PortConfig pCfg:cmd.getPortConfigs()){ - if (pCfg.isAdd()) { - ipList.add(pCfg.getSourceIp()); - } - } - boolean success = true; - String result; - result = configureIpFirewall(ipList); - if (result !=null) - success = false; - return new Answer(cmd, success, result); - } - protected GetStorageStatsAnswer execute(final GetStorageStatsCommand cmd) { + private Answer execute(SecStorageFirewallCfgCommand cmd) { + if (!_inSystemVM){ + return new Answer(cmd, true, null); + } + + List ipList = new ArrayList(); + + for (PortConfig pCfg:cmd.getPortConfigs()){ + if (pCfg.isAdd()) { + ipList.add(pCfg.getSourceIp()); + } + } + boolean success = true; + String result; + result = configureIpFirewall(ipList); + if (result !=null) + success = false; + + return new Answer(cmd, success, result); + } + + protected GetStorageStatsAnswer execute(final GetStorageStatsCommand cmd) { final long usedSize = getUsedSize(); final long totalSize = getTotalSize(); if (usedSize == -1 || totalSize == -1) { - return new GetStorageStatsAnswer(cmd, "Unable to get storage stats"); + return new GetStorageStatsAnswer(cmd, "Unable to get storage stats"); } else { - return new GetStorageStatsAnswer(cmd, totalSize, usedSize) ; + return new GetStorageStatsAnswer(cmd, totalSize, usedSize) ; } } - + @Override public String getRootDir(ssCommand cmd){ return null; } - + protected Answer execute(final DeleteTemplateCommand cmd) { String relativeTemplatePath = cmd.getTemplatePath(); String parent = _parent; @@ -278,15 +277,15 @@ public class CifsSecondaryStorageResource extends ServerResourceBase implements } return new Answer(cmd, true, null); } - + protected long getUsedSize() { - return _storage.getUsedSpace(_parent); + return _storage.getUsedSpace(_parent); } - + protected long getTotalSize() { - return _storage.getTotalSpace(_parent); + return _storage.getTotalSpace(_parent); } - + protected long convertFilesystemSize(final String size) { if (size == null || size.isEmpty()) { return -1; @@ -305,25 +304,25 @@ public class CifsSecondaryStorageResource extends ServerResourceBase implements return (long)(Double.parseDouble(size.substring(0, size.length() - 1)) * multiplier); } - + @Override public Type getType() { return Host.Type.SecondaryStorage; } - + @Override public PingCommand getCurrentStatus(final long id) { return new PingStorageCommand(Host.Type.Storage, id, new HashMap()); } - + @Override public boolean configure(String name, Map params) throws ConfigurationException { - _eth1ip = (String)params.get("eth1ip"); + _eth1ip = (String)params.get("eth1ip"); if (_eth1ip != null) { //can only happen inside service vm - params.put("private.network.device", "eth1"); + params.put("private.network.device", "eth1"); } else { - s_logger.warn("Wait, what's going on? eth1ip is null!!"); + s_logger.warn("Wait, what's going on? eth1ip is null!!"); } String eth2ip = (String) params.get("eth2ip"); if (eth2ip != null) { @@ -331,23 +330,23 @@ public class CifsSecondaryStorageResource extends ServerResourceBase implements } _publicIp = (String) params.get("eth2ip"); _hostname = (String) params.get("name"); - + super.configure(name, params); - + _params = params; String value = (String)params.get("scripts.timeout"); _timeout = NumbersUtil.parseInt(value, 1440) * 1000; - + _storage = (StorageLayer)params.get(StorageLayer.InstanceConfigKey); if (_storage == null) { value = (String)params.get(StorageLayer.ClassConfigKey); if (value == null) { value = "com.cloud.storage.JavaStorageLayer"; } - + try { Class clazz = Class.forName(value); - _storage = (StorageLayer)ComponentLocator.inject(clazz); + _storage = (StorageLayer)ComponentContext.inject(clazz); _storage.configure("StorageLayer", params); } catch (ClassNotFoundException e) { throw new ConfigurationException("Unable to find class " + value); @@ -362,30 +361,30 @@ public class CifsSecondaryStorageResource extends ServerResourceBase implements if (_configSslScr != null) { s_logger.info("config_auth.sh found in " + _configAuthScr); } - + _configIpFirewallScr = Script.findScript(getDefaultScriptsDir(), "ipfirewall.sh"); if (_configIpFirewallScr != null) { s_logger.info("_configIpFirewallScr found in " + _configIpFirewallScr); } - + _guid = (String)params.get("guid"); if (_guid == null) { throw new ConfigurationException("Unable to find the guid"); } - + _dc = (String)params.get("zone"); if (_dc == null) { throw new ConfigurationException("Unable to find the zone"); } _pod = (String)params.get("pod"); - + _instance = (String)params.get("instance"); _mountParent = (String)params.get("mount.parent"); if (_mountParent == null) { _mountParent = File.separator + "mnt"; } - + if (_instance != null) { _mountParent = _mountParent + File.separator + _instance; } @@ -394,63 +393,63 @@ public class CifsSecondaryStorageResource extends ServerResourceBase implements if (_nfsPath == null) { throw new ConfigurationException("Unable to find mount.path"); } - - + + String inSystemVM = (String)params.get("secondary.storage.vm"); if (inSystemVM == null || "true".equalsIgnoreCase(inSystemVM)) { - _inSystemVM = true; + _inSystemVM = true; _localgw = (String)params.get("localgw"); if (_localgw != null) { //can only happen inside service vm - _eth1mask = (String)params.get("eth1mask"); - String internalDns1 = (String)params.get("dns1"); - String internalDns2 = (String)params.get("dns2"); + _eth1mask = (String)params.get("eth1mask"); + String internalDns1 = (String)params.get("dns1"); + String internalDns2 = (String)params.get("dns2"); - if (internalDns1 == null) { - s_logger.warn("No DNS entry found during configuration of NfsSecondaryStorage"); - } else { - addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, internalDns1); - } - - String mgmtHost = (String)params.get("host"); - String nfsHost = NfsUtils.getHostPart(_nfsPath); - if (nfsHost == null) { - s_logger.error("Invalid or corrupt nfs url " + _nfsPath); - throw new CloudRuntimeException("Unable to determine host part of nfs path"); - } - try { - InetAddress nfsHostAddr = InetAddress.getByName(nfsHost); - nfsHost = nfsHostAddr.getHostAddress(); - } catch (UnknownHostException uhe) { - s_logger.error("Unable to resolve nfs host " + nfsHost); - throw new CloudRuntimeException("Unable to resolve nfs host to an ip address " + nfsHost); - } - addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, nfsHost); - addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, mgmtHost); - if (internalDns2 != null) { - addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, internalDns2); - } + if (internalDns1 == null) { + s_logger.warn("No DNS entry found during configuration of NfsSecondaryStorage"); + } else { + addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, internalDns1); + } + + String mgmtHost = (String)params.get("host"); + String nfsHost = NfsUtils.getHostPart(_nfsPath); + if (nfsHost == null) { + s_logger.error("Invalid or corrupt nfs url " + _nfsPath); + throw new CloudRuntimeException("Unable to determine host part of nfs path"); + } + try { + InetAddress nfsHostAddr = InetAddress.getByName(nfsHost); + nfsHost = nfsHostAddr.getHostAddress(); + } catch (UnknownHostException uhe) { + s_logger.error("Unable to resolve nfs host " + nfsHost); + throw new CloudRuntimeException("Unable to resolve nfs host to an ip address " + nfsHost); + } + addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, nfsHost); + addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, mgmtHost); + if (internalDns2 != null) { + addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, internalDns2); + } } String useSsl = (String)params.get("sslcopy"); if (useSsl != null) { - _sslCopy = Boolean.parseBoolean(useSsl); - if (_sslCopy) { - configureSSL(); - } + _sslCopy = Boolean.parseBoolean(useSsl); + if (_sslCopy) { + configureSSL(); + } } - startAdditionalServices(); - _params.put("install.numthreads", "50"); - _params.put("secondary.storage.vm", "true"); + startAdditionalServices(); + _params.put("install.numthreads", "50"); + _params.put("secondary.storage.vm", "true"); } _parent = mount(_nfsPath, _mountParent); if (_parent == null) { throw new ConfigurationException("Unable to create mount point"); } - - + + s_logger.info("Mount point established at " + _parent); - + try { _params.put("template.parent", _parent); _params.put(StorageLayer.InstanceConfigKey, _storage); @@ -464,98 +463,98 @@ public class CifsSecondaryStorageResource extends ServerResourceBase implements } return true; } - + private void startAdditionalServices() { - Script command = new Script("/bin/bash", s_logger); - command.add("-c"); - command.add("if [ -f /etc/init.d/ssh ]; then service ssh restart; else service sshd restart; fi "); - String result = command.execute(); - if (result != null) { - s_logger.warn("Error in starting sshd service err=" + result ); - } - command = new Script("/bin/bash", s_logger); - command.add("-c"); - command.add("iptables -I INPUT -i eth1 -p tcp -m state --state NEW -m tcp --dport 3922 -j ACCEPT"); - result = command.execute(); - if (result != null) { - s_logger.warn("Error in opening up ssh port err=" + result ); - } - } - - private void addRouteToInternalIpOrCidr(String localgw, String eth1ip, String eth1mask, String destIpOrCidr) { - s_logger.debug("addRouteToInternalIp: localgw=" + localgw + ", eth1ip=" + eth1ip + ", eth1mask=" + eth1mask + ",destIp=" + destIpOrCidr); - if (destIpOrCidr == null) { - s_logger.debug("addRouteToInternalIp: destIp is null"); - return; - } - if (!NetUtils.isValidIp(destIpOrCidr) && !NetUtils.isValidCIDR(destIpOrCidr)){ - s_logger.warn(" destIp is not a valid ip address or cidr destIp=" + destIpOrCidr); - return; - } - boolean inSameSubnet = false; - if (NetUtils.isValidIp(destIpOrCidr)) { - if (eth1ip != null && eth1mask != null) { - inSameSubnet = NetUtils.sameSubnet(eth1ip, destIpOrCidr, eth1mask); - } else { - s_logger.warn("addRouteToInternalIp: unable to determine same subnet: _eth1ip=" + eth1ip + ", dest ip=" + destIpOrCidr + ", _eth1mask=" + eth1mask); - } - } else { - inSameSubnet = NetUtils.isNetworkAWithinNetworkB(destIpOrCidr, NetUtils.ipAndNetMaskToCidr(eth1ip, eth1mask)); - } - if (inSameSubnet) { - s_logger.debug("addRouteToInternalIp: dest ip " + destIpOrCidr + " is in the same subnet as eth1 ip " + eth1ip); - return; - } - Script command = new Script("/bin/bash", s_logger); - command.add("-c"); - command.add("ip route delete " + destIpOrCidr); - command.execute(); - command = new Script("/bin/bash", s_logger); - command.add("-c"); - command.add("ip route add " + destIpOrCidr + " via " + localgw); - String result = command.execute(); - if (result != null) { - s_logger.warn("Error in configuring route to internal ip err=" + result ); - } else { - s_logger.debug("addRouteToInternalIp: added route to internal ip=" + destIpOrCidr + " via " + localgw); - } + Script command = new Script("/bin/bash", s_logger); + command.add("-c"); + command.add("if [ -f /etc/init.d/ssh ]; then service ssh restart; else service sshd restart; fi "); + String result = command.execute(); + if (result != null) { + s_logger.warn("Error in starting sshd service err=" + result ); + } + command = new Script("/bin/bash", s_logger); + command.add("-c"); + command.add("iptables -I INPUT -i eth1 -p tcp -m state --state NEW -m tcp --dport 3922 -j ACCEPT"); + result = command.execute(); + if (result != null) { + s_logger.warn("Error in opening up ssh port err=" + result ); + } } - private void configureSSL() { - Script command = new Script(_configSslScr); - command.add(_publicIp); - command.add(_hostname); - String result = command.execute(); - if (result != null) { - s_logger.warn("Unable to configure httpd to use ssl"); - } - } - - private String configureAuth(String user, String passwd) { - Script command = new Script(_configAuthScr); - command.add(user); - command.add(passwd); - String result = command.execute(); - if (result != null) { - s_logger.warn("Unable to configure httpd to use auth"); - } - return result; - } - - private String configureIpFirewall(List ipList){ - Script command = new Script(_configIpFirewallScr); - for (String ip : ipList){ - command.add(ip); - } - - String result = command.execute(); - if (result != null) { - s_logger.warn("Unable to configure firewall for command : " +command); - } - return result; - } - - protected String mount(String path, String parent) { + private void addRouteToInternalIpOrCidr(String localgw, String eth1ip, String eth1mask, String destIpOrCidr) { + s_logger.debug("addRouteToInternalIp: localgw=" + localgw + ", eth1ip=" + eth1ip + ", eth1mask=" + eth1mask + ",destIp=" + destIpOrCidr); + if (destIpOrCidr == null) { + s_logger.debug("addRouteToInternalIp: destIp is null"); + return; + } + if (!NetUtils.isValidIp(destIpOrCidr) && !NetUtils.isValidCIDR(destIpOrCidr)){ + s_logger.warn(" destIp is not a valid ip address or cidr destIp=" + destIpOrCidr); + return; + } + boolean inSameSubnet = false; + if (NetUtils.isValidIp(destIpOrCidr)) { + if (eth1ip != null && eth1mask != null) { + inSameSubnet = NetUtils.sameSubnet(eth1ip, destIpOrCidr, eth1mask); + } else { + s_logger.warn("addRouteToInternalIp: unable to determine same subnet: _eth1ip=" + eth1ip + ", dest ip=" + destIpOrCidr + ", _eth1mask=" + eth1mask); + } + } else { + inSameSubnet = NetUtils.isNetworkAWithinNetworkB(destIpOrCidr, NetUtils.ipAndNetMaskToCidr(eth1ip, eth1mask)); + } + if (inSameSubnet) { + s_logger.debug("addRouteToInternalIp: dest ip " + destIpOrCidr + " is in the same subnet as eth1 ip " + eth1ip); + return; + } + Script command = new Script("/bin/bash", s_logger); + command.add("-c"); + command.add("ip route delete " + destIpOrCidr); + command.execute(); + command = new Script("/bin/bash", s_logger); + command.add("-c"); + command.add("ip route add " + destIpOrCidr + " via " + localgw); + String result = command.execute(); + if (result != null) { + s_logger.warn("Error in configuring route to internal ip err=" + result ); + } else { + s_logger.debug("addRouteToInternalIp: added route to internal ip=" + destIpOrCidr + " via " + localgw); + } + } + + private void configureSSL() { + Script command = new Script(_configSslScr); + command.add(_publicIp); + command.add(_hostname); + String result = command.execute(); + if (result != null) { + s_logger.warn("Unable to configure httpd to use ssl"); + } + } + + private String configureAuth(String user, String passwd) { + Script command = new Script(_configAuthScr); + command.add(user); + command.add(passwd); + String result = command.execute(); + if (result != null) { + s_logger.warn("Unable to configure httpd to use auth"); + } + return result; + } + + private String configureIpFirewall(List ipList){ + Script command = new Script(_configIpFirewallScr); + for (String ip : ipList){ + command.add(ip); + } + + String result = command.execute(); + if (result != null) { + s_logger.warn("Unable to configure firewall for command : " +command); + } + return result; + } + + protected String mount(String path, String parent) { String mountPoint = null; for (int i = 0; i < 10; i++) { String mntPt = parent + File.separator + Integer.toHexString(_rand.nextInt(Integer.MAX_VALUE)); @@ -568,29 +567,29 @@ public class CifsSecondaryStorageResource extends ServerResourceBase implements } s_logger.debug("Unable to create mount: " + mntPt); } - + if (mountPoint == null) { s_logger.warn("Unable to create a mount point"); return null; } - + Script script = null; String result = null; script = new Script(!_inSystemVM, "umount", _timeout, s_logger); script.add(path); result = script.execute(); - + if( _parent != null ) { script = new Script("rmdir", _timeout, s_logger); script.add(_parent); result = script.execute(); } - + Script command = new Script(!_inSystemVM, "mount", _timeout, s_logger); command.add("-t", "cifs"); if (_inSystemVM) { - //Fedora Core 12 errors out with any -o option executed from java - //command.add("-o", "soft,timeo=133,retrans=2147483647,tcp,acdirmax=0,acdirmin=0"); + //Fedora Core 12 errors out with any -o option executed from java + //command.add("-o", "soft,timeo=133,retrans=2147483647,tcp,acdirmax=0,acdirmin=0"); } String tok[] = path.split(":"); //command.add(path); @@ -601,25 +600,25 @@ public class CifsSecondaryStorageResource extends ServerResourceBase implements s_logger.warn("Unable to mount " + path + " due to " + result); File file = new File(mountPoint); if (file.exists()) - file.delete(); + file.delete(); return null; } - - - + + + // XXX: Adding the check for creation of snapshots dir here. Might have to move it somewhere more logical later. if (!checkForSnapshotsDir(mountPoint)) { - return null; + return null; } - + // Create the volumes dir if (!checkForVolumesDir(mountPoint)) { - return null; + return null; } - + return mountPoint; } - + @Override public boolean start() { return true; @@ -633,14 +632,14 @@ public class CifsSecondaryStorageResource extends ServerResourceBase implements @Override public StartupCommand[] initialize() { /*disconnected(); - + _parent = mount(_nfsPath, _mountParent); - + if( _parent == null ) { s_logger.warn("Unable to mount the nfs server"); return null; } - + try { _params.put("template.parent", _parent); _params.put(StorageLayer.InstanceConfigKey, _storage); @@ -650,12 +649,12 @@ public class CifsSecondaryStorageResource extends ServerResourceBase implements s_logger.warn("Caught problem while configuring folers", e); return null; }*/ - + final StartupStorageCommand cmd = new StartupStorageCommand(_parent, StoragePoolType.NetworkFilesystem, getTotalSize(), new HashMap()); - + cmd.setResourceType(Storage.StorageResourceType.SECONDARY_STORAGE); cmd.setIqn(null); - + fillNetworkInformation(cmd); cmd.setDataCenter(_dc); cmd.setPod(_pod); @@ -687,38 +686,38 @@ public class CifsSecondaryStorageResource extends ServerResourceBase implements String snapshotsDirLocation = mountPoint + File.separator + "snapshots"; return createDir("snapshots", snapshotsDirLocation, mountPoint); } - - protected boolean checkForVolumesDir(String mountPoint) { - String volumesDirLocation = mountPoint + "/" + "volumes"; - return createDir("volumes", volumesDirLocation, mountPoint); - } - - protected boolean createDir(String dirName, String dirLocation, String mountPoint) { - boolean dirExists = false; - - File dir = new File(dirLocation); - if (dir.exists()) { - if (dir.isDirectory()) { - s_logger.debug(dirName + " already exists on secondary storage, and is mounted at " + mountPoint); - dirExists = true; - } else { - if (dir.delete() && _storage.mkdir(dirLocation)) { - dirExists = true; - } - } - } else if (_storage.mkdir(dirLocation)) { - dirExists = true; - } - if (dirExists) { - s_logger.info(dirName + " directory created/exists on Secondary Storage."); - } else { - s_logger.info(dirName + " directory does not exist on Secondary Storage."); - } - - return dirExists; + protected boolean checkForVolumesDir(String mountPoint) { + String volumesDirLocation = mountPoint + "/" + "volumes"; + return createDir("volumes", volumesDirLocation, mountPoint); } - + + protected boolean createDir(String dirName, String dirLocation, String mountPoint) { + boolean dirExists = false; + + File dir = new File(dirLocation); + if (dir.exists()) { + if (dir.isDirectory()) { + s_logger.debug(dirName + " already exists on secondary storage, and is mounted at " + mountPoint); + dirExists = true; + } else { + if (dir.delete() && _storage.mkdir(dirLocation)) { + dirExists = true; + } + } + } else if (_storage.mkdir(dirLocation)) { + dirExists = true; + } + + if (dirExists) { + s_logger.info(dirName + " directory created/exists on Secondary Storage."); + } else { + s_logger.info(dirName + " directory does not exist on Secondary Storage."); + } + + return dirExists; + } + @Override protected String getDefaultScriptsDir() { return "./scripts/storage/secondary"; diff --git a/core/src/com/cloud/storage/resource/LocalSecondaryStorageResource.java b/core/src/com/cloud/storage/resource/LocalSecondaryStorageResource.java index d9c69f8b151..b86fe6c6c2d 100644 --- a/core/src/com/cloud/storage/resource/LocalSecondaryStorageResource.java +++ b/core/src/com/cloud/storage/resource/LocalSecondaryStorageResource.java @@ -19,7 +19,6 @@ package com.cloud.storage.resource; import java.util.HashMap; import java.util.Map; - import javax.naming.ConfigurationException; import org.apache.log4j.Logger; @@ -36,11 +35,11 @@ import com.cloud.agent.api.ReadyCommand; import com.cloud.agent.api.SecStorageSetupCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupStorageCommand; +import com.cloud.agent.api.storage.DownloadCommand; +import com.cloud.agent.api.storage.DownloadProgressCommand; import com.cloud.agent.api.storage.ListTemplateAnswer; import com.cloud.agent.api.storage.ListTemplateCommand; import com.cloud.agent.api.storage.ssCommand; -import com.cloud.agent.api.storage.DownloadCommand; -import com.cloud.agent.api.storage.DownloadProgressCommand; import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.resource.ServerResourceBase; @@ -50,39 +49,38 @@ import com.cloud.storage.StorageLayer; import com.cloud.storage.template.DownloadManager; import com.cloud.storage.template.DownloadManagerImpl; import com.cloud.storage.template.TemplateInfo; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.component.ComponentContext; public class LocalSecondaryStorageResource extends ServerResourceBase implements SecondaryStorageResource { private static final Logger s_logger = Logger.getLogger(LocalSecondaryStorageResource.class); int _timeout; - + String _instance; String _parent; - + String _dc; String _pod; String _guid; - + StorageLayer _storage; - + DownloadManager _dlMgr; - + @Override public void disconnected() { } - + @Override public String getRootDir(ssCommand cmd){ return getRootDir(); - + } - + public String getRootDir() { return _parent; } - + @Override public Answer executeRequest(Command cmd) { if (cmd instanceof DownloadProgressCommand) { @@ -103,7 +101,7 @@ public class LocalSecondaryStorageResource extends ServerResourceBase implements return Answer.createUnsupportedCommandAnswer(cmd); } } - + private Answer execute(ComputeChecksumCommand cmd) { return new Answer(cmd, false, null); } @@ -119,13 +117,13 @@ public class LocalSecondaryStorageResource extends ServerResourceBase implements public Type getType() { return Host.Type.LocalSecondaryStorage; } - + @Override public PingCommand getCurrentStatus(final long id) { return new PingStorageCommand(Host.Type.Storage, id, new HashMap()); } - - + + @Override @SuppressWarnings("unchecked") public boolean configure(String name, Map params) throws ConfigurationException { @@ -135,30 +133,30 @@ public class LocalSecondaryStorageResource extends ServerResourceBase implements if (_guid == null) { throw new ConfigurationException("Unable to find the guid"); } - + _dc = (String)params.get("zone"); if (_dc == null) { throw new ConfigurationException("Unable to find the zone"); } _pod = (String)params.get("pod"); - + _instance = (String)params.get("instance"); _parent = (String)params.get("mount.path"); if (_parent == null) { throw new ConfigurationException("No directory specified."); } - + _storage = (StorageLayer)params.get(StorageLayer.InstanceConfigKey); if (_storage == null) { String value = (String)params.get(StorageLayer.ClassConfigKey); if (value == null) { value = "com.cloud.storage.JavaStorageLayer"; } - + try { Class clazz = (Class)Class.forName(value); - _storage = ComponentLocator.inject(clazz); + _storage = ComponentContext.inject(clazz); } catch (ClassNotFoundException e) { throw new ConfigurationException("Unable to find class " + value); } @@ -168,15 +166,15 @@ public class LocalSecondaryStorageResource extends ServerResourceBase implements s_logger.warn("Unable to create the directory " + _parent); throw new ConfigurationException("Unable to create the directory " + _parent); } - + s_logger.info("Mount point established at " + _parent); params.put("template.parent", _parent); params.put(StorageLayer.InstanceConfigKey, _storage); - + _dlMgr = new DownloadManagerImpl(); _dlMgr.configure("DownloadManager", params); - + return true; } @@ -192,7 +190,7 @@ public class LocalSecondaryStorageResource extends ServerResourceBase implements @Override public StartupCommand[] initialize() { - + final StartupStorageCommand cmd = new StartupStorageCommand(_parent, StoragePoolType.Filesystem, 1024l*1024l*1024l*1024l, _dlMgr.gatherTemplateInfo(_parent)); cmd.setResourceType(Storage.StorageResourceType.LOCAL_SECONDARY_STORAGE); cmd.setIqn("local://"); @@ -202,10 +200,10 @@ public class LocalSecondaryStorageResource extends ServerResourceBase implements cmd.setGuid(_guid); cmd.setName(_guid); cmd.setVersion(LocalSecondaryStorageResource.class.getPackage().getImplementationVersion()); - + return new StartupCommand [] {cmd}; } - + @Override protected String getDefaultScriptsDir() { return "scripts/storage/secondary"; diff --git a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java b/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java index a4bea9df2b4..7c105489d72 100755 --- a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java +++ b/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java @@ -71,7 +71,6 @@ import com.cloud.agent.api.SecStorageFirewallCfgCommand.PortConfig; import com.cloud.agent.api.SecStorageSetupAnswer; import com.cloud.agent.api.SecStorageSetupCommand; import com.cloud.agent.api.SecStorageSetupCommand.Certificates; -import com.cloud.agent.api.StartupSecondaryStorageCommand; import com.cloud.agent.api.SecStorageVMSetupCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupSecondaryStorageCommand; @@ -109,7 +108,7 @@ import com.cloud.utils.NumbersUtil; import com.cloud.utils.S3Utils; import com.cloud.utils.S3Utils.FileNamingStrategy; import com.cloud.utils.S3Utils.ObjectNamingStrategy; -import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.utils.script.OutputInterpreter; @@ -117,7 +116,7 @@ import com.cloud.utils.script.Script; import com.cloud.vm.SecondaryStorageVm; public class NfsSecondaryStorageResource extends ServerResourceBase implements - SecondaryStorageResource { +SecondaryStorageResource { private static final Logger s_logger = Logger .getLogger(NfsSecondaryStorageResource.class); @@ -126,7 +125,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements private static final String SNAPSHOT_ROOT_DIR = "snapshots"; int _timeout; - + String _instance; String _dc; String _pod; @@ -136,23 +135,23 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements StorageLayer _storage; boolean _inSystemVM = false; boolean _sslCopy = false; - + DownloadManager _dlMgr; UploadManager _upldMgr; - private String _configSslScr; - private String _configAuthScr; - private String _configIpFirewallScr; - private String _publicIp; - private String _hostname; - private String _localgw; - private String _eth1mask; - private String _eth1ip; - private String _storageIp; - private String _storageNetmask; - private String _storageGateway; - private List nfsIps = new ArrayList(); - final private String _parent = "/mnt/SecStorage"; - final private String _tmpltDir = "/var/cloudstack/template"; + private String _configSslScr; + private String _configAuthScr; + private String _configIpFirewallScr; + private String _publicIp; + private String _hostname; + private String _localgw; + private String _eth1mask; + private String _eth1ip; + private String _storageIp; + private String _storageNetmask; + private String _storageGateway; + private final List nfsIps = new ArrayList(); + final private String _parent = "/mnt/SecStorage"; + final private String _tmpltDir = "/var/cloudstack/template"; final private String _tmpltpp = "template.properties"; @Override public void disconnected() { @@ -171,19 +170,19 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements } else if(cmd instanceof DeleteEntityDownloadURLCommand){ return _upldMgr.handleDeleteEntityDownloadURLCommand((DeleteEntityDownloadURLCommand)cmd); } else if (cmd instanceof GetStorageStatsCommand) { - return execute((GetStorageStatsCommand)cmd); + return execute((GetStorageStatsCommand)cmd); } else if (cmd instanceof CheckHealthCommand) { return new CheckHealthAnswer((CheckHealthCommand)cmd, true); } else if (cmd instanceof DeleteTemplateCommand) { - return execute((DeleteTemplateCommand) cmd); + return execute((DeleteTemplateCommand) cmd); } else if (cmd instanceof DeleteVolumeCommand) { - return execute((DeleteVolumeCommand) cmd); + return execute((DeleteVolumeCommand) cmd); }else if (cmd instanceof ReadyCommand) { return new ReadyAnswer((ReadyCommand)cmd); } else if (cmd instanceof SecStorageFirewallCfgCommand){ - return execute((SecStorageFirewallCfgCommand)cmd); + return execute((SecStorageFirewallCfgCommand)cmd); } else if (cmd instanceof SecStorageVMSetupCommand){ - return execute((SecStorageVMSetupCommand)cmd); + return execute((SecStorageVMSetupCommand)cmd); } else if (cmd instanceof SecStorageSetupCommand){ return execute((SecStorageSetupCommand)cmd); } else if (cmd instanceof ComputeChecksumCommand){ @@ -218,7 +217,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements return Answer.createUnsupportedCommandAnswer(cmd); } } - + @SuppressWarnings("unchecked") private String determineS3TemplateDirectory(final Long accountId, final Long templateId) { @@ -254,7 +253,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements "Unable to create directory " + "download directory %1$s for download of template id " + "%2$s from S3.", downloadDirectory.getName(), - templateId); + templateId); s_logger.error(errMsg); return new Answer(cmd, false, errMsg); } @@ -262,11 +261,11 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements getDirectory(s3, s3.getBucketName(), determineS3TemplateDirectory(accountId, templateId), downloadDirectory, new FileNamingStrategy() { - @Override - public String determineFileName(final String key) { - return substringAfterLast(key, S3Utils.SEPARATOR); - } - }); + @Override + public String determineFileName(final String key) { + return substringAfterLast(key, S3Utils.SEPARATOR); + } + }); return new Answer(cmd, true, format("Successfully downloaded " + "template id %1$s from S3 to directory %2$s", templateId, @@ -395,23 +394,23 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements final String bucket = s3.getBucketName(); putDirectory(s3, bucket, _storage.getFile(templatePath), new FilenameFilter() { - @Override - public boolean accept(final File directory, - final String fileName) { - return !fileName.startsWith("."); - } - }, new ObjectNamingStrategy() { - @Override - public String determineKey(final File file) { - s_logger.debug(String - .format("Determining key using account id %1$s and template id %2$s", - accountId, templateId)); - return join( - asList(determineS3TemplateDirectory( - accountId, templateId), file - .getName()), S3Utils.SEPARATOR); - } - }); + @Override + public boolean accept(final File directory, + final String fileName) { + return !fileName.startsWith("."); + } + }, new ObjectNamingStrategy() { + @Override + public String determineKey(final File file) { + s_logger.debug(String + .format("Determining key using account id %1$s and template id %2$s", + accountId, templateId)); + return join( + asList(determineS3TemplateDirectory( + accountId, templateId), file + .getName()), S3Utils.SEPARATOR); + } + }); return new Answer( cmd, @@ -623,7 +622,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements command.add("-c"); command.add("/usr/bin/python /usr/local/cloud/systemvm/scripts/storage/secondary/swift -A " + swift.getUrl() + " -U " + swift.getAccount() + ":" + swift.getUserName() + " -K " + swift.getKey() - + " delete " + container + " " + object); + + " delete " + container + " " + object); OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); String result = command.execute(parser); if (result != null) { @@ -678,61 +677,61 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements executeWithNoWaitLock(determineSnapshotLockId(accountId, volumeId), new Callable() { + @Override + public Void call() throws Exception { + + final String directoryName = determineSnapshotLocalDirectory( + secondaryStorageUrl, accountId, volumeId); + + String result = createLocalDir(directoryName); + if (result != null) { + throw new InternalErrorException( + format("Failed to create directory %1$s during S3 snapshot download.", + directoryName)); + } + + final String snapshotFileName = determineSnapshotBackupFilename(cmd + .getSnapshotUuid()); + final String key = determineSnapshotS3Key( + accountId, volumeId, snapshotFileName); + final File targetFile = S3Utils.getFile(s3, + s3.getBucketName(), key, + _storage.getFile(directoryName), + new FileNamingStrategy() { + @Override - public Void call() throws Exception { - - final String directoryName = determineSnapshotLocalDirectory( - secondaryStorageUrl, accountId, volumeId); - - String result = createLocalDir(directoryName); - if (result != null) { - throw new InternalErrorException( - format("Failed to create directory %1$s during S3 snapshot download.", - directoryName)); - } - - final String snapshotFileName = determineSnapshotBackupFilename(cmd - .getSnapshotUuid()); - final String key = determineSnapshotS3Key( - accountId, volumeId, snapshotFileName); - final File targetFile = S3Utils.getFile(s3, - s3.getBucketName(), key, - _storage.getFile(directoryName), - new FileNamingStrategy() { - - @Override - public String determineFileName( - String key) { - return snapshotFileName; - } - - }); - - if (cmd.getParent() != null) { - - final String parentPath = join( - File.pathSeparator, directoryName, - determineSnapshotBackupFilename(cmd - .getParent())); - result = setVhdParent( - targetFile.getAbsolutePath(), - parentPath); - if (result != null) { - throw new InternalErrorException( - format("Failed to set the parent for backup %1$s to %2$s due to %3$s.", - targetFile - .getAbsolutePath(), - parentPath, result)); - } - - } - - return null; - + public String determineFileName( + String key) { + return snapshotFileName; } }); + if (cmd.getParent() != null) { + + final String parentPath = join( + File.pathSeparator, directoryName, + determineSnapshotBackupFilename(cmd + .getParent())); + result = setVhdParent( + targetFile.getAbsolutePath(), + parentPath); + if (result != null) { + throw new InternalErrorException( + format("Failed to set the parent for backup %1$s to %2$s due to %3$s.", + targetFile + .getAbsolutePath(), + parentPath, result)); + } + + } + + return null; + + } + + }); + return new Answer( cmd, true, @@ -820,7 +819,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements } private Answer execute(ComputeChecksumCommand cmd) { - + String relativeTemplatePath = cmd.getTemplatePath(); String parent = getRootDir(cmd); @@ -841,8 +840,8 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements if(s_logger.isDebugEnabled()){ s_logger.debug("parent path " +parent+ " relative template path " +relativeTemplatePath ); } - - + + try { digest = MessageDigest.getInstance("MD5"); is = new FileInputStream(f); @@ -855,7 +854,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements if(s_logger.isDebugEnabled()){ s_logger.debug("Successfully calculated checksum for file " +absoluteTemplatePath+ " - " +checksum ); } - + }catch(IOException e) { String logMsg = "Unable to process file for MD5 - " + absoluteTemplatePath; s_logger.error(logMsg); @@ -865,11 +864,11 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements } finally { try { - if(is != null) - is.close(); + if(is != null) + is.close(); } catch (IOException e) { if(s_logger.isDebugEnabled()){ - s_logger.debug("Could not close the file " +absoluteTemplatePath); + s_logger.debug("Could not close the file " +absoluteTemplatePath); } return new Answer(cmd, false, checksum); } @@ -879,38 +878,38 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements } private void configCerts(Certificates certs) { - if (certs == null) { - configureSSL(); - } else { - String prvKey = certs.getPrivKey(); - String pubCert = certs.getPrivCert(); - String certChain = certs.getCertChain(); - - try { - File prvKeyFile = File.createTempFile("prvkey", null); - String prvkeyPath = prvKeyFile.getAbsolutePath(); - BufferedWriter out = new BufferedWriter(new FileWriter(prvKeyFile)); - out.write(prvKey); - out.close(); - - File pubCertFile = File.createTempFile("pubcert", null); - String pubCertFilePath = pubCertFile.getAbsolutePath(); - - out = new BufferedWriter(new FileWriter(pubCertFile)); - out.write(pubCert); - out.close(); - - configureSSL(prvkeyPath, pubCertFilePath, null); - - prvKeyFile.delete(); - pubCertFile.delete(); - - } catch (IOException e) { - s_logger.debug("Failed to config ssl: " + e.toString()); - } - } + if (certs == null) { + configureSSL(); + } else { + String prvKey = certs.getPrivKey(); + String pubCert = certs.getPrivCert(); + String certChain = certs.getCertChain(); + + try { + File prvKeyFile = File.createTempFile("prvkey", null); + String prvkeyPath = prvKeyFile.getAbsolutePath(); + BufferedWriter out = new BufferedWriter(new FileWriter(prvKeyFile)); + out.write(prvKey); + out.close(); + + File pubCertFile = File.createTempFile("pubcert", null); + String pubCertFilePath = pubCertFile.getAbsolutePath(); + + out = new BufferedWriter(new FileWriter(pubCertFile)); + out.write(pubCert); + out.close(); + + configureSSL(prvkeyPath, pubCertFilePath, null); + + prvKeyFile.delete(); + pubCertFile.delete(); + + } catch (IOException e) { + s_logger.debug("Failed to config ssl: " + e.toString()); + } + } } - + private Answer execute(SecStorageSetupCommand cmd) { if (!_inSystemVM){ return new Answer(cmd, true, null); @@ -930,7 +929,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements mount(root, nfsPath); configCerts(cmd.getCerts()); - + nfsIps.add(nfsHostIp); return new SecStorageSetupAnswer(dir); } catch (Exception e) { @@ -940,7 +939,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements } } - + private String deleteSnapshotBackupFromLocalFileSystem( final String secondaryStorageUrl, final Long accountId, final Long volumeId, final String name, final Boolean deleteAllFlag) { @@ -1072,7 +1071,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements return new Answer(cmd, false, errMsg); } } - + Map swiftListTemplate(SwiftTO swift) { String[] containers = swiftList(swift, "", ""); if (containers == null) { @@ -1103,9 +1102,9 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements } } return tmpltInfos; - + } - + private Answer execute(ListTemplateCommand cmd) { if (!_inSystemVM){ return new Answer(cmd, true, null); @@ -1119,50 +1118,50 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements return new ListTemplateAnswer(cmd.getSecUrl(), templateInfos); } } - + private Answer execute(ListVolumeCommand cmd) { if (!_inSystemVM){ return new Answer(cmd, true, null); } - + String root = getRootDir(cmd.getSecUrl()); Map templateInfos = _dlMgr.gatherVolumeInfo(root); return new ListVolumeAnswer(cmd.getSecUrl(), templateInfos); - - } - - private Answer execute(SecStorageVMSetupCommand cmd) { - if (!_inSystemVM){ - return new Answer(cmd, true, null); - } - boolean success = true; - StringBuilder result = new StringBuilder(); - for (String cidr: cmd.getAllowedInternalSites()) { - if (nfsIps.contains(cidr)) { - /* - * if the internal download ip is the same with secondary storage ip, adding internal sites will flush - * ip route to nfs through storage ip. - */ - continue; - } - String tmpresult = allowOutgoingOnPrivate(cidr); - if (tmpresult != null) { - result.append(", ").append(tmpresult); - success = false; - } - } - if (success) { - if (cmd.getCopyPassword() != null && cmd.getCopyUserName() != null) { - String tmpresult = configureAuth(cmd.getCopyUserName(), cmd.getCopyPassword()); - if (tmpresult != null) { - result.append("Failed to configure auth for copy ").append(tmpresult); - success = false; - } - } - } - return new Answer(cmd, success, result.toString()); - } + } + + private Answer execute(SecStorageVMSetupCommand cmd) { + if (!_inSystemVM){ + return new Answer(cmd, true, null); + } + boolean success = true; + StringBuilder result = new StringBuilder(); + for (String cidr: cmd.getAllowedInternalSites()) { + if (nfsIps.contains(cidr)) { + /* + * if the internal download ip is the same with secondary storage ip, adding internal sites will flush + * ip route to nfs through storage ip. + */ + continue; + } + String tmpresult = allowOutgoingOnPrivate(cidr); + if (tmpresult != null) { + result.append(", ").append(tmpresult); + success = false; + } + } + if (success) { + if (cmd.getCopyPassword() != null && cmd.getCopyUserName() != null) { + String tmpresult = configureAuth(cmd.getCopyUserName(), cmd.getCopyPassword()); + if (tmpresult != null) { + result.append("Failed to configure auth for copy ").append(tmpresult); + success = false; + } + } + } + return new Answer(cmd, success, result.toString()); + + } private String setVhdParent(String lFullPath, String pFullPath) { Script command = new Script("/bin/bash", s_logger); @@ -1217,55 +1216,55 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements } public String allowOutgoingOnPrivate(String destCidr) { - - Script command = new Script("/bin/bash", s_logger); - String intf = "eth1"; - command.add("-c"); - command.add("iptables -I OUTPUT -o " + intf + " -d " + destCidr + " -p tcp -m state --state NEW -m tcp -j ACCEPT"); - String result = command.execute(); - if (result != null) { - s_logger.warn("Error in allowing outgoing to " + destCidr + ", err=" + result ); - return "Error in allowing outgoing to " + destCidr + ", err=" + result; - } - - addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, destCidr); - - return null; - } - - private Answer execute(SecStorageFirewallCfgCommand cmd) { - if (!_inSystemVM){ - return new Answer(cmd, true, null); - } + Script command = new Script("/bin/bash", s_logger); + String intf = "eth1"; + command.add("-c"); + command.add("iptables -I OUTPUT -o " + intf + " -d " + destCidr + " -p tcp -m state --state NEW -m tcp -j ACCEPT"); - List ipList = new ArrayList(); - - for (PortConfig pCfg:cmd.getPortConfigs()){ - if (pCfg.isAdd()) { - ipList.add(pCfg.getSourceIp()); - } - } - boolean success = true; - String result; - result = configureIpFirewall(ipList, cmd.getIsAppendAIp()); - if (result !=null) - success = false; + String result = command.execute(); + if (result != null) { + s_logger.warn("Error in allowing outgoing to " + destCidr + ", err=" + result ); + return "Error in allowing outgoing to " + destCidr + ", err=" + result; + } - return new Answer(cmd, success, result); - } + addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, destCidr); - protected GetStorageStatsAnswer execute(final GetStorageStatsCommand cmd) { - String rootDir = getRootDir(cmd.getSecUrl()); + return null; + } + + private Answer execute(SecStorageFirewallCfgCommand cmd) { + if (!_inSystemVM){ + return new Answer(cmd, true, null); + } + + List ipList = new ArrayList(); + + for (PortConfig pCfg:cmd.getPortConfigs()){ + if (pCfg.isAdd()) { + ipList.add(pCfg.getSourceIp()); + } + } + boolean success = true; + String result; + result = configureIpFirewall(ipList, cmd.getIsAppendAIp()); + if (result !=null) + success = false; + + return new Answer(cmd, success, result); + } + + protected GetStorageStatsAnswer execute(final GetStorageStatsCommand cmd) { + String rootDir = getRootDir(cmd.getSecUrl()); final long usedSize = getUsedSize(rootDir); final long totalSize = getTotalSize(rootDir); if (usedSize == -1 || totalSize == -1) { - return new GetStorageStatsAnswer(cmd, "Unable to get storage stats"); + return new GetStorageStatsAnswer(cmd, "Unable to get storage stats"); } else { - return new GetStorageStatsAnswer(cmd, totalSize, usedSize) ; + return new GetStorageStatsAnswer(cmd, totalSize, usedSize) ; } } - + protected Answer execute(final DeleteTemplateCommand cmd) { String relativeTemplatePath = cmd.getTemplatePath(); String parent = getRootDir(cmd); @@ -1313,7 +1312,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements } return new Answer(cmd, true, null); } - + protected Answer execute(final DeleteVolumeCommand cmd) { String relativeVolumePath = cmd.getVolumePath(); String parent = getRootDir(cmd); @@ -1361,7 +1360,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements } return new Answer(cmd, true, null); } - + Answer execute(CleanupSnapshotBackupCommand cmd) { String parent = getRootDir(cmd.getSecondaryStoragePoolURL()); if (!parent.endsWith(File.separator)) { @@ -1409,22 +1408,22 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements throw new CloudRuntimeException(msg); } } - - + + @Override public String getRootDir(ssCommand cmd){ return getRootDir(cmd.getSecUrl()); - + } - + protected long getUsedSize(String rootDir) { return _storage.getUsedSpace(rootDir); } - + protected long getTotalSize(String rootDir) { - return _storage.getTotalSpace(rootDir); + return _storage.getTotalSpace(rootDir); } - + protected long convertFilesystemSize(final String size) { if (size == null || size.isEmpty()) { return -1; @@ -1443,29 +1442,29 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements return (long)(Double.parseDouble(size.substring(0, size.length() - 1)) * multiplier); } - + @Override public Type getType() { - if(SecondaryStorageVm.Role.templateProcessor.toString().equals(_role)) - return Host.Type.SecondaryStorage; - - return Host.Type.SecondaryStorageCmdExecutor; + if(SecondaryStorageVm.Role.templateProcessor.toString().equals(_role)) + return Host.Type.SecondaryStorage; + + return Host.Type.SecondaryStorageCmdExecutor; } - + @Override public PingCommand getCurrentStatus(final long id) { return new PingStorageCommand(Host.Type.Storage, id, new HashMap()); } - + @Override public boolean configure(String name, Map params) throws ConfigurationException { - _eth1ip = (String)params.get("eth1ip"); + _eth1ip = (String)params.get("eth1ip"); _eth1mask = (String)params.get("eth1mask"); if (_eth1ip != null) { //can only happen inside service vm - params.put("private.network.device", "eth1"); + params.put("private.network.device", "eth1"); } else { - s_logger.warn("Wait, what's going on? eth1ip is null!!"); + s_logger.warn("Wait, what's going on? eth1ip is null!!"); } String eth2ip = (String) params.get("eth2ip"); if (eth2ip != null) { @@ -1473,29 +1472,29 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements } _publicIp = (String) params.get("eth2ip"); _hostname = (String) params.get("name"); - + _storageIp = (String) params.get("storageip"); if (_storageIp == null) { - s_logger.warn("Wait, there is no storageip in /proc/cmdline, something wrong!"); + s_logger.warn("Wait, there is no storageip in /proc/cmdline, something wrong!"); } _storageNetmask = (String) params.get("storagenetmask"); _storageGateway = (String) params.get("storagegateway"); super.configure(name, params); - + _params = params; String value = (String)params.get("scripts.timeout"); _timeout = NumbersUtil.parseInt(value, 1440) * 1000; - + _storage = (StorageLayer)params.get(StorageLayer.InstanceConfigKey); if (_storage == null) { value = (String)params.get(StorageLayer.ClassConfigKey); if (value == null) { value = "com.cloud.storage.JavaStorageLayer"; } - + try { Class clazz = Class.forName(value); - _storage = (StorageLayer)ComponentLocator.inject(clazz); + _storage = (StorageLayer)ComponentContext.inject(clazz); _storage.configure("StorageLayer", params); } catch (ClassNotFoundException e) { throw new ConfigurationException("Unable to find class " + value); @@ -1511,34 +1510,34 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements if (_configSslScr != null) { s_logger.info("config_auth.sh found in " + _configAuthScr); } - + _configIpFirewallScr = Script.findScript(getDefaultScriptsDir(), "ipfirewall.sh"); if (_configIpFirewallScr != null) { s_logger.info("_configIpFirewallScr found in " + _configIpFirewallScr); } - + _role = (String)params.get("role"); if(_role == null) - _role = SecondaryStorageVm.Role.templateProcessor.toString(); + _role = SecondaryStorageVm.Role.templateProcessor.toString(); s_logger.info("Secondary storage runs in role " + _role); - + _guid = (String)params.get("guid"); if (_guid == null) { throw new ConfigurationException("Unable to find the guid"); } - + _dc = (String)params.get("zone"); if (_dc == null) { throw new ConfigurationException("Unable to find the zone"); } _pod = (String)params.get("pod"); - + _instance = (String)params.get("instance"); - - + + String inSystemVM = (String)params.get("secondary.storage.vm"); if (inSystemVM == null || "true".equalsIgnoreCase(inSystemVM)) { - _inSystemVM = true; + _inSystemVM = true; _localgw = (String)params.get("localgw"); if (_localgw != null) { // can only happen inside service vm String mgmtHost = (String) params.get("host"); @@ -1557,12 +1556,12 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements } } - - startAdditionalServices(); - _params.put("install.numthreads", "50"); - _params.put("secondary.storage.vm", "true"); + + startAdditionalServices(); + _params.put("install.numthreads", "50"); + _params.put("secondary.storage.vm", "true"); } - + try { _params.put(StorageLayer.InstanceConfigKey, _storage); _dlMgr = new DownloadManagerImpl(); @@ -1575,114 +1574,114 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements } return true; } - + private void startAdditionalServices() { - Script command = new Script("/bin/bash", s_logger); - command.add("-c"); - command.add("if [ -f /etc/init.d/ssh ]; then service ssh restart; else service sshd restart; fi "); - String result = command.execute(); - if (result != null) { - s_logger.warn("Error in starting sshd service err=" + result ); - } - command = new Script("/bin/bash", s_logger); - command.add("-c"); - command.add("iptables -I INPUT -i eth1 -p tcp -m state --state NEW -m tcp --dport 3922 -j ACCEPT"); - result = command.execute(); - if (result != null) { - s_logger.warn("Error in opening up ssh port err=" + result ); - } - } - - private void addRouteToInternalIpOrCidr(String localgw, String eth1ip, String eth1mask, String destIpOrCidr) { - s_logger.debug("addRouteToInternalIp: localgw=" + localgw + ", eth1ip=" + eth1ip + ", eth1mask=" + eth1mask + ",destIp=" + destIpOrCidr); - if (destIpOrCidr == null) { - s_logger.debug("addRouteToInternalIp: destIp is null"); - return; - } - if (!NetUtils.isValidIp(destIpOrCidr) && !NetUtils.isValidCIDR(destIpOrCidr)){ - s_logger.warn(" destIp is not a valid ip address or cidr destIp=" + destIpOrCidr); - return; - } - boolean inSameSubnet = false; - if (NetUtils.isValidIp(destIpOrCidr)) { - if (eth1ip != null && eth1mask != null) { - inSameSubnet = NetUtils.sameSubnet(eth1ip, destIpOrCidr, eth1mask); - } else { - s_logger.warn("addRouteToInternalIp: unable to determine same subnet: _eth1ip=" + eth1ip + ", dest ip=" + destIpOrCidr + ", _eth1mask=" + eth1mask); - } - } else { - inSameSubnet = NetUtils.isNetworkAWithinNetworkB(destIpOrCidr, NetUtils.ipAndNetMaskToCidr(eth1ip, eth1mask)); - } - if (inSameSubnet) { - s_logger.debug("addRouteToInternalIp: dest ip " + destIpOrCidr + " is in the same subnet as eth1 ip " + eth1ip); - return; - } - Script command = new Script("/bin/bash", s_logger); - command.add("-c"); - command.add("ip route delete " + destIpOrCidr); - command.execute(); - command = new Script("/bin/bash", s_logger); - command.add("-c"); - command.add("ip route add " + destIpOrCidr + " via " + localgw); - String result = command.execute(); - if (result != null) { - s_logger.warn("Error in configuring route to internal ip err=" + result ); - } else { - s_logger.debug("addRouteToInternalIp: added route to internal ip=" + destIpOrCidr + " via " + localgw); - } + Script command = new Script("/bin/bash", s_logger); + command.add("-c"); + command.add("if [ -f /etc/init.d/ssh ]; then service ssh restart; else service sshd restart; fi "); + String result = command.execute(); + if (result != null) { + s_logger.warn("Error in starting sshd service err=" + result ); + } + command = new Script("/bin/bash", s_logger); + command.add("-c"); + command.add("iptables -I INPUT -i eth1 -p tcp -m state --state NEW -m tcp --dport 3922 -j ACCEPT"); + result = command.execute(); + if (result != null) { + s_logger.warn("Error in opening up ssh port err=" + result ); + } } - private void configureSSL() { - Script command = new Script(_configSslScr); - command.add("-i", _publicIp); - command.add("-h", _hostname); - String result = command.execute(); - if (result != null) { - s_logger.warn("Unable to configure httpd to use ssl"); - } - } - - private void configureSSL(String prvkeyPath, String prvCertPath, String certChainPath) { - Script command = new Script(_configSslScr); - command.add("-i", _publicIp); - command.add("-h", _hostname); - command.add("-k", prvkeyPath); - command.add("-p", prvCertPath); - if (certChainPath != null) { - command.add("-t", certChainPath); - } - String result = command.execute(); - if (result != null) { - s_logger.warn("Unable to configure httpd to use ssl"); - } - } - - private String configureAuth(String user, String passwd) { - Script command = new Script(_configAuthScr); - command.add(user); - command.add(passwd); - String result = command.execute(); - if (result != null) { - s_logger.warn("Unable to configure httpd to use auth"); - } - return result; - } - - private String configureIpFirewall(List ipList, boolean isAppend){ - Script command = new Script(_configIpFirewallScr); - command.add(String.valueOf(isAppend)); - for (String ip : ipList){ - command.add(ip); - } - - String result = command.execute(); - if (result != null) { - s_logger.warn("Unable to configure firewall for command : " +command); - } - return result; - } - - protected String mount(String root, String nfsPath) { + private void addRouteToInternalIpOrCidr(String localgw, String eth1ip, String eth1mask, String destIpOrCidr) { + s_logger.debug("addRouteToInternalIp: localgw=" + localgw + ", eth1ip=" + eth1ip + ", eth1mask=" + eth1mask + ",destIp=" + destIpOrCidr); + if (destIpOrCidr == null) { + s_logger.debug("addRouteToInternalIp: destIp is null"); + return; + } + if (!NetUtils.isValidIp(destIpOrCidr) && !NetUtils.isValidCIDR(destIpOrCidr)){ + s_logger.warn(" destIp is not a valid ip address or cidr destIp=" + destIpOrCidr); + return; + } + boolean inSameSubnet = false; + if (NetUtils.isValidIp(destIpOrCidr)) { + if (eth1ip != null && eth1mask != null) { + inSameSubnet = NetUtils.sameSubnet(eth1ip, destIpOrCidr, eth1mask); + } else { + s_logger.warn("addRouteToInternalIp: unable to determine same subnet: _eth1ip=" + eth1ip + ", dest ip=" + destIpOrCidr + ", _eth1mask=" + eth1mask); + } + } else { + inSameSubnet = NetUtils.isNetworkAWithinNetworkB(destIpOrCidr, NetUtils.ipAndNetMaskToCidr(eth1ip, eth1mask)); + } + if (inSameSubnet) { + s_logger.debug("addRouteToInternalIp: dest ip " + destIpOrCidr + " is in the same subnet as eth1 ip " + eth1ip); + return; + } + Script command = new Script("/bin/bash", s_logger); + command.add("-c"); + command.add("ip route delete " + destIpOrCidr); + command.execute(); + command = new Script("/bin/bash", s_logger); + command.add("-c"); + command.add("ip route add " + destIpOrCidr + " via " + localgw); + String result = command.execute(); + if (result != null) { + s_logger.warn("Error in configuring route to internal ip err=" + result ); + } else { + s_logger.debug("addRouteToInternalIp: added route to internal ip=" + destIpOrCidr + " via " + localgw); + } + } + + private void configureSSL() { + Script command = new Script(_configSslScr); + command.add("-i", _publicIp); + command.add("-h", _hostname); + String result = command.execute(); + if (result != null) { + s_logger.warn("Unable to configure httpd to use ssl"); + } + } + + private void configureSSL(String prvkeyPath, String prvCertPath, String certChainPath) { + Script command = new Script(_configSslScr); + command.add("-i", _publicIp); + command.add("-h", _hostname); + command.add("-k", prvkeyPath); + command.add("-p", prvCertPath); + if (certChainPath != null) { + command.add("-t", certChainPath); + } + String result = command.execute(); + if (result != null) { + s_logger.warn("Unable to configure httpd to use ssl"); + } + } + + private String configureAuth(String user, String passwd) { + Script command = new Script(_configAuthScr); + command.add(user); + command.add(passwd); + String result = command.execute(); + if (result != null) { + s_logger.warn("Unable to configure httpd to use auth"); + } + return result; + } + + private String configureIpFirewall(List ipList, boolean isAppend){ + Script command = new Script(_configIpFirewallScr); + command.add(String.valueOf(isAppend)); + for (String ip : ipList){ + command.add(ip); + } + + String result = command.execute(); + if (result != null) { + s_logger.warn("Unable to configure firewall for command : " +command); + } + return result; + } + + protected String mount(String root, String nfsPath) { File file = new File(root); if (!file.exists()) { if (_storage.mkdir(root)) { @@ -1691,8 +1690,8 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements s_logger.debug("Unable to create mount point: " + root); return null; } - } - + } + Script script = null; String result = null; script = new Script(!_inSystemVM, "mount", _timeout, s_logger); @@ -1705,12 +1704,12 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements return root; } } - + Script command = new Script(!_inSystemVM, "mount", _timeout, s_logger); command.add("-t", "nfs"); if (_inSystemVM) { - //Fedora Core 12 errors out with any -o option executed from java - command.add("-o", "soft,timeo=133,retrans=2147483647,tcp,acdirmax=0,acdirmin=0"); + //Fedora Core 12 errors out with any -o option executed from java + command.add("-o", "soft,timeo=133,retrans=2147483647,tcp,acdirmax=0,acdirmin=0"); } command.add(nfsPath); command.add(root); @@ -1719,23 +1718,23 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements s_logger.warn("Unable to mount " + nfsPath + " due to " + result); file = new File(root); if (file.exists()) - file.delete(); + file.delete(); return null; } - + // XXX: Adding the check for creation of snapshots dir here. Might have to move it somewhere more logical later. if (!checkForSnapshotsDir(root)) { - return null; + return null; } - + // Create the volumes dir if (!checkForVolumesDir(root)) { - return null; + return null; } - + return root; } - + @Override public boolean start() { return true; @@ -1748,12 +1747,12 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements @Override public StartupCommand[] initialize() { - + final StartupSecondaryStorageCommand cmd = new StartupSecondaryStorageCommand(); fillNetworkInformation(cmd); if(_publicIp != null) cmd.setPublicIpAddress(_publicIp); - + Script command = new Script("/bin/bash", s_logger); command.add("-c"); command.add("ln -sf " + _parent + " /var/www/html/copy"); @@ -1769,38 +1768,38 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements String snapshotsDirLocation = mountPoint + File.separator + "snapshots"; return createDir("snapshots", snapshotsDirLocation, mountPoint); } - - protected boolean checkForVolumesDir(String mountPoint) { - String volumesDirLocation = mountPoint + "/" + "volumes"; - return createDir("volumes", volumesDirLocation, mountPoint); - } - - protected boolean createDir(String dirName, String dirLocation, String mountPoint) { - boolean dirExists = false; - - File dir = new File(dirLocation); - if (dir.exists()) { - if (dir.isDirectory()) { - s_logger.debug(dirName + " already exists on secondary storage, and is mounted at " + mountPoint); - dirExists = true; - } else { - if (dir.delete() && _storage.mkdir(dirLocation)) { - dirExists = true; - } - } - } else if (_storage.mkdir(dirLocation)) { - dirExists = true; - } - if (dirExists) { - s_logger.info(dirName + " directory created/exists on Secondary Storage."); - } else { - s_logger.info(dirName + " directory does not exist on Secondary Storage."); - } - - return dirExists; + protected boolean checkForVolumesDir(String mountPoint) { + String volumesDirLocation = mountPoint + "/" + "volumes"; + return createDir("volumes", volumesDirLocation, mountPoint); } - + + protected boolean createDir(String dirName, String dirLocation, String mountPoint) { + boolean dirExists = false; + + File dir = new File(dirLocation); + if (dir.exists()) { + if (dir.isDirectory()) { + s_logger.debug(dirName + " already exists on secondary storage, and is mounted at " + mountPoint); + dirExists = true; + } else { + if (dir.delete() && _storage.mkdir(dirLocation)) { + dirExists = true; + } + } + } else if (_storage.mkdir(dirLocation)) { + dirExists = true; + } + + if (dirExists) { + s_logger.info(dirName + " directory created/exists on Secondary Storage."); + } else { + s_logger.info(dirName + " directory does not exist on Secondary Storage."); + } + + return dirExists; + } + @Override protected String getDefaultScriptsDir() { return "./scripts/storage/secondary"; diff --git a/core/src/com/cloud/storage/template/DownloadManagerImpl.java b/core/src/com/cloud/storage/template/DownloadManagerImpl.java index f8b075d4a06..d5ed71e8459 100755 --- a/core/src/com/cloud/storage/template/DownloadManagerImpl.java +++ b/core/src/com/cloud/storage/template/DownloadManagerImpl.java @@ -29,8 +29,8 @@ import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; -import java.util.Enumeration; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; @@ -43,7 +43,6 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; -import com.cloud.agent.api.Answer; import com.cloud.agent.api.storage.DownloadAnswer; import com.cloud.agent.api.storage.DownloadCommand; import com.cloud.agent.api.storage.DownloadCommand.Proxy; @@ -60,10 +59,6 @@ import com.cloud.storage.template.Processor.FormatInfo; import com.cloud.storage.template.TemplateDownloader.DownloadCompleteCallback; import com.cloud.storage.template.TemplateDownloader.Status; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.component.Adapter; -import com.cloud.utils.component.Adapters; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.component.LegacyComponentLocator.ComponentInfo; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; @@ -72,7 +67,7 @@ import com.cloud.utils.script.Script; public class DownloadManagerImpl implements DownloadManager { private String _name; StorageLayer _storage; - Adapters _processors; + Map _processors; public class Completion implements DownloadCompleteCallback { private final String jobId; @@ -94,14 +89,14 @@ public class DownloadManagerImpl implements DownloadManager { private final boolean hvm; private final ImageFormat format; private String tmpltPath; - private String description; + private final String description; private String checksum; - private Long accountId; - private String installPathPrefix; + private final Long accountId; + private final String installPathPrefix; private long templatesize; private long templatePhysicalSize; - private long id; - private ResourceType resourceType; + private final long id; + private final ResourceType resourceType; public DownloadJob(TemplateDownloader td, String jobId, long id, String tmpltName, ImageFormat format, boolean hvm, Long accountId, String descr, String cksum, String installPathPrefix, ResourceType resourceType) { super(); @@ -160,10 +155,10 @@ public class DownloadManagerImpl implements DownloadManager { } public ResourceType getResourceType() { - return resourceType; - } + return resourceType; + } - public void setTmpltPath(String tmpltPath) { + public void setTmpltPath(String tmpltPath) { this.tmpltPath = tmpltPath; } @@ -205,9 +200,9 @@ public class DownloadManagerImpl implements DownloadManager { public long getTemplatePhysicalSize() { return templatePhysicalSize; } - + public void setCheckSum(String checksum) { - this.checksum = checksum; + this.checksum = checksum; } } @@ -216,7 +211,7 @@ public class DownloadManagerImpl implements DownloadManager { private String _volumeDir; private String createTmpltScr; private String createVolScr; - private Adapters processors; + private List processors; private ExecutorService threadPool; @@ -278,9 +273,9 @@ public class DownloadManagerImpl implements DownloadManager { break; } } - + private String computeCheckSum(File f) { - byte[] buffer = new byte[8192]; + byte[] buffer = new byte[8192]; int read = 0; MessageDigest digest; String checksum = null; @@ -296,16 +291,16 @@ public class DownloadManagerImpl implements DownloadManager { checksum = String.format("%032x",bigInt); return checksum; }catch(IOException e) { - return null; + return null; }catch (NoSuchAlgorithmException e) { - return null; + return null; } finally { try { - if(is != null) - is.close(); + if(is != null) + is.close(); } catch (IOException e) { - return null; + return null; } } } @@ -320,17 +315,17 @@ public class DownloadManagerImpl implements DownloadManager { TemplateDownloader td = dnld.getTemplateDownloader(); String resourcePath = null; ResourceType resourceType = dnld.getResourceType(); - + // once template path is set, remove the parent dir so that the template is installed with a relative path String finalResourcePath = ""; if (resourceType == ResourceType.TEMPLATE){ - finalResourcePath += _templateDir + File.separator + dnld.getAccountId() + File.separator + dnld.getId() + File.separator; - resourcePath = dnld.getInstallPathPrefix() + dnld.getAccountId() + File.separator + dnld.getId() + File.separator;// dnld.getTmpltName(); + finalResourcePath += _templateDir + File.separator + dnld.getAccountId() + File.separator + dnld.getId() + File.separator; + resourcePath = dnld.getInstallPathPrefix() + dnld.getAccountId() + File.separator + dnld.getId() + File.separator;// dnld.getTmpltName(); }else { - finalResourcePath += _volumeDir + File.separator + dnld.getId() + File.separator; - resourcePath = dnld.getInstallPathPrefix() + dnld.getId() + File.separator;// dnld.getTmpltName(); + finalResourcePath += _volumeDir + File.separator + dnld.getId() + File.separator; + resourcePath = dnld.getInstallPathPrefix() + dnld.getId() + File.separator;// dnld.getTmpltName(); } - + _storage.mkdirs(resourcePath); dnld.setTmpltPath(finalResourcePath); @@ -389,9 +384,9 @@ public class DownloadManagerImpl implements DownloadManager { // Set permissions for template/volume.properties String propertiesFile = resourcePath; if (resourceType == ResourceType.TEMPLATE){ - propertiesFile += "/template.properties"; + propertiesFile += "/template.properties"; }else{ - propertiesFile += "/volume.properties"; + propertiesFile += "/volume.properties"; } File templateProperties = new File(propertiesFile); _storage.setWorldReadableAndWriteable(templateProperties); @@ -405,9 +400,9 @@ public class DownloadManagerImpl implements DownloadManager { return "Unable to download due to " + e.getMessage(); } - Enumeration en = _processors.enumeration(); - while (en.hasMoreElements()) { - Processor processor = en.nextElement(); + Iterator en = _processors.values().iterator(); + while (en.hasNext()) { + Processor processor = en.next(); FormatInfo info = null; try { @@ -423,7 +418,7 @@ public class DownloadManagerImpl implements DownloadManager { break; } } - + if (!loc.save()) { s_logger.warn("Cleaning up because we're unable to save the formats"); loc.purge(); @@ -450,9 +445,9 @@ public class DownloadManagerImpl implements DownloadManager { String jobId = uuid.toString(); String tmpDir = ""; if(resourceType == ResourceType.TEMPLATE){ - tmpDir = installPathPrefix + File.separator + accountId + File.separator + id; + tmpDir = installPathPrefix + File.separator + accountId + File.separator + id; }else { - tmpDir = installPathPrefix + File.separator + id; + tmpDir = installPathPrefix + File.separator + id; } try { @@ -463,7 +458,7 @@ public class DownloadManagerImpl implements DownloadManager { } // TO DO - define constant for volume properties. File file = ResourceType.TEMPLATE == resourceType ? _storage.getFile(tmpDir + File.separator + TemplateLocation.Filename) : - _storage.getFile(tmpDir + File.separator + "volume.properties"); + _storage.getFile(tmpDir + File.separator + "volume.properties"); if ( file.exists() ) { file.delete(); } @@ -524,9 +519,9 @@ public class DownloadManagerImpl implements DownloadManager { } return 0; } - + public String getDownloadCheckSum(String jobId) { - DownloadJob dj = jobs.get(jobId); + DownloadJob dj = jobs.get(jobId); if (dj != null) { return dj.getChecksum(); } @@ -589,7 +584,7 @@ public class DownloadManagerImpl implements DownloadManager { @Override public DownloadAnswer handleDownloadCommand(SecondaryStorageResource resource, DownloadCommand cmd) { - ResourceType resourceType = cmd.getResourceType(); + ResourceType resourceType = cmd.getResourceType(); if (cmd instanceof DownloadProgressCommand) { return handleDownloadProgressCmd( resource, (DownloadProgressCommand) cmd); } @@ -604,9 +599,9 @@ public class DownloadManagerImpl implements DownloadManager { String installPathPrefix = null; if (ResourceType.TEMPLATE == resourceType){ - installPathPrefix = resource.getRootDir(cmd) + File.separator + _templateDir; + installPathPrefix = resource.getRootDir(cmd) + File.separator + _templateDir; }else { - installPathPrefix = resource.getRootDir(cmd) + File.separator + _volumeDir; + installPathPrefix = resource.getRootDir(cmd) + File.separator + _volumeDir; } String user = null; @@ -693,10 +688,10 @@ public class DownloadManagerImpl implements DownloadManager { } - + private List listVolumes(String rootdir) { List result = new ArrayList(); - + Script script = new Script(listVolScr, s_logger); script.add("-r", rootdir); ZfsPathParser zpp = new ZfsPathParser(rootdir); @@ -705,12 +700,12 @@ public class DownloadManagerImpl implements DownloadManager { s_logger.info("found " + zpp.getPaths().size() + " volumes" + zpp.getPaths()); return result; } - - - + + + private List listTemplates(String rootdir) { List result = new ArrayList(); - + Script script = new Script(listTmpltScr, s_logger); script.add("-r", rootdir); ZfsPathParser zpp = new ZfsPathParser(rootdir); @@ -724,11 +719,11 @@ public class DownloadManagerImpl implements DownloadManager { public Map gatherTemplateInfo(String rootDir) { Map result = new HashMap(); String templateDir = rootDir + File.separator + _templateDir; - + if (! _storage.exists(templateDir)) { _storage.mkdirs(templateDir); } - + List publicTmplts = listTemplates(templateDir); for (String tmplt : publicTmplts) { String path = tmplt.substring(0, tmplt.lastIndexOf(File.separator)); @@ -746,18 +741,18 @@ public class DownloadManagerImpl implements DownloadManager { } TemplateInfo tInfo = loc.getTemplateInfo(); - + if ((tInfo.size == tInfo.physicalSize) && (tInfo.installPath.endsWith(ImageFormat.OVA.getFileExtension()))) { - try { - Processor processor = _processors.get("VMDK Processor"); - VmdkProcessor vmdkProcessor = (VmdkProcessor)processor; - long vSize = vmdkProcessor.getTemplateVirtualSize(path, tInfo.installPath.substring(tInfo.installPath.lastIndexOf(File.separator) + 1)); - tInfo.size = vSize; - loc.updateVirtualSize(vSize); - loc.save(); - } catch (Exception e) { - s_logger.error("Unable to get the virtual size of the template: " + tInfo.installPath + " due to " + e.getMessage()); - } + try { + Processor processor = _processors.get("VMDK Processor"); + VmdkProcessor vmdkProcessor = (VmdkProcessor)processor; + long vSize = vmdkProcessor.getTemplateVirtualSize(path, tInfo.installPath.substring(tInfo.installPath.lastIndexOf(File.separator) + 1)); + tInfo.size = vSize; + loc.updateVirtualSize(vSize); + loc.save(); + } catch (Exception e) { + s_logger.error("Unable to get the virtual size of the template: " + tInfo.installPath + " due to " + e.getMessage()); + } } result.put(tInfo.templateName, tInfo); @@ -777,52 +772,52 @@ public class DownloadManagerImpl implements DownloadManager { return result; } - @Override - public Map gatherVolumeInfo(String rootDir) { - Map result = new HashMap(); - String volumeDir = rootDir + File.separator + _volumeDir; - - if (! _storage.exists(volumeDir)) { - _storage.mkdirs(volumeDir); - } - - List vols = listVolumes(volumeDir); - for (String vol : vols) { - String path = vol.substring(0, vol.lastIndexOf(File.separator)); - TemplateLocation loc = new TemplateLocation(_storage, path); - try { - if (!loc.load()) { - s_logger.warn("Post download installation was not completed for " + path); - //loc.purge(); - _storage.cleanup(path, volumeDir); - continue; - } - } catch (IOException e) { - s_logger.warn("Unable to load volume location " + path, e); - continue; - } + @Override + public Map gatherVolumeInfo(String rootDir) { + Map result = new HashMap(); + String volumeDir = rootDir + File.separator + _volumeDir; - TemplateInfo vInfo = loc.getTemplateInfo(); - - if ((vInfo.size == vInfo.physicalSize) && (vInfo.installPath.endsWith(ImageFormat.OVA.getFileExtension()))) { - try { - Processor processor = _processors.get("VMDK Processor"); - VmdkProcessor vmdkProcessor = (VmdkProcessor)processor; - long vSize = vmdkProcessor.getTemplateVirtualSize(path, vInfo.installPath.substring(vInfo.installPath.lastIndexOf(File.separator) + 1)); - vInfo.size = vSize; - loc.updateVirtualSize(vSize); - loc.save(); - } catch (Exception e) { - s_logger.error("Unable to get the virtual size of the volume: " + vInfo.installPath + " due to " + e.getMessage()); - } - } + if (! _storage.exists(volumeDir)) { + _storage.mkdirs(volumeDir); + } + + List vols = listVolumes(volumeDir); + for (String vol : vols) { + String path = vol.substring(0, vol.lastIndexOf(File.separator)); + TemplateLocation loc = new TemplateLocation(_storage, path); + try { + if (!loc.load()) { + s_logger.warn("Post download installation was not completed for " + path); + //loc.purge(); + _storage.cleanup(path, volumeDir); + continue; + } + } catch (IOException e) { + s_logger.warn("Unable to load volume location " + path, e); + continue; + } + + TemplateInfo vInfo = loc.getTemplateInfo(); + + if ((vInfo.size == vInfo.physicalSize) && (vInfo.installPath.endsWith(ImageFormat.OVA.getFileExtension()))) { + try { + Processor processor = _processors.get("VMDK Processor"); + VmdkProcessor vmdkProcessor = (VmdkProcessor)processor; + long vSize = vmdkProcessor.getTemplateVirtualSize(path, vInfo.installPath.substring(vInfo.installPath.lastIndexOf(File.separator) + 1)); + vInfo.size = vSize; + loc.updateVirtualSize(vSize); + loc.save(); + } catch (Exception e) { + s_logger.error("Unable to get the virtual size of the volume: " + vInfo.installPath + " due to " + e.getMessage()); + } + } + + result.put(vInfo.getId(), vInfo); + s_logger.debug("Added volume name: " + vInfo.templateName + ", path: " + vol); + } + return result; + } - result.put(vInfo.getId(), vInfo); - s_logger.debug("Added volume name: " + vInfo.templateName + ", path: " + vol); - } - return result; - } - private int deleteDownloadDirectories(File downloadPath, int deleted) { try { if (downloadPath.exists()) { @@ -881,7 +876,7 @@ public class DownloadManagerImpl implements DownloadManager { String value = null; - _storage = (StorageLayer) params.get(StorageLayer.InstanceConfigKey); + _storage = (StorageLayer)params.get(StorageLayer.InstanceConfigKey); if (_storage == null) { value = (String) params.get(StorageLayer.ClassConfigKey); if (value == null) { @@ -891,10 +886,14 @@ public class DownloadManagerImpl implements DownloadManager { Class clazz; try { clazz = (Class) Class.forName(value); + _storage = clazz.newInstance(); } catch (ClassNotFoundException e) { throw new ConfigurationException("Unable to instantiate " + value); + } catch (InstantiationException e) { + throw new ConfigurationException("Unable to instantiate " + value); + } catch (IllegalAccessException e) { + throw new ConfigurationException("Unable to instantiate " + value); } - _storage = ComponentLocator.inject(clazz); } String useSsl = (String)params.get("sslcopy"); if (useSsl != null) { @@ -943,29 +942,27 @@ public class DownloadManagerImpl implements DownloadManager { } s_logger.info("createvolume.sh found in " + createVolScr); - List> processors = new ArrayList>(); + _processors = new HashMap(); Processor processor = new VhdProcessor(); processor.configure("VHD Processor", params); - processors.add(new ComponentInfo("VHD Processor", VhdProcessor.class, processor)); + _processors.put("VHD Processor", processor); processor = new IsoProcessor(); processor.configure("ISO Processor", params); - processors.add(new ComponentInfo("ISO Processor", IsoProcessor.class, processor)); + _processors.put("ISO Processor", processor); processor = new QCOW2Processor(); processor.configure("QCOW2 Processor", params); - processors.add(new ComponentInfo("QCOW2 Processor", QCOW2Processor.class, processor)); + _processors.put("QCOW2 Processor", processor); processor = new VmdkProcessor(); processor.configure("VMDK Processor", params); - processors.add(new ComponentInfo("VMDK Processor", VmdkProcessor.class, processor)); + _processors.put("VMDK Processor", processor); processor = new RawImageProcessor(); processor.configure("Raw Image Processor", params); - processors.add(new ComponentInfo("Raw Image Processor", RawImageProcessor.class, processor)); - - _processors = new Adapters("processors", processors); + _processors.put("Raw Image Processor", processor); _templateDir = (String) params.get("public.templates.root.dir"); if (_templateDir == null) { @@ -1047,5 +1044,5 @@ public class DownloadManagerImpl implements DownloadManager { return; } } - + } diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/ClusterDaoImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/ClusterDaoImpl.java index bd4a5ae2939..f33c6730f1a 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/ClusterDaoImpl.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/ClusterDaoImpl.java @@ -32,109 +32,107 @@ import org.apache.cloudstack.engine.datacenter.entity.api.db.HostPodVO; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; - import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.org.Grouping; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.UpdateBuilder; import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.UpdateBuilder; import com.cloud.utils.exception.CloudRuntimeException; @Component(value="EngineClusterDao") @Local(value=ClusterDao.class) public class ClusterDaoImpl extends GenericDaoBase implements ClusterDao { - private static final Logger s_logger = Logger.getLogger(ClusterDaoImpl.class); - + private static final Logger s_logger = Logger.getLogger(ClusterDaoImpl.class); + protected final SearchBuilder PodSearch; protected final SearchBuilder HyTypeWithoutGuidSearch; protected final SearchBuilder AvailHyperSearch; protected final SearchBuilder ZoneSearch; protected final SearchBuilder ZoneHyTypeSearch; protected SearchBuilder StateChangeSearch; - protected SearchBuilder UUIDSearch; - + protected SearchBuilder UUIDSearch; + private static final String GET_POD_CLUSTER_MAP_PREFIX = "SELECT pod_id, id FROM cloud.cluster WHERE cluster.id IN( "; private static final String GET_POD_CLUSTER_MAP_SUFFIX = " )"; - + @Inject protected HostPodDao _hostPodDao; - + protected ClusterDaoImpl() { super(); - + HyTypeWithoutGuidSearch = createSearchBuilder(); HyTypeWithoutGuidSearch.and("hypervisorType", HyTypeWithoutGuidSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ); HyTypeWithoutGuidSearch.and("guid", HyTypeWithoutGuidSearch.entity().getGuid(), SearchCriteria.Op.NULL); HyTypeWithoutGuidSearch.done(); - + ZoneHyTypeSearch = createSearchBuilder(); ZoneHyTypeSearch.and("hypervisorType", ZoneHyTypeSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ); ZoneHyTypeSearch.and("dataCenterId", ZoneHyTypeSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); ZoneHyTypeSearch.done(); - + PodSearch = createSearchBuilder(); PodSearch.and("pod", PodSearch.entity().getPodId(), SearchCriteria.Op.EQ); PodSearch.and("name", PodSearch.entity().getName(), SearchCriteria.Op.EQ); PodSearch.done(); - + ZoneSearch = createSearchBuilder(); ZoneSearch.and("dataCenterId", ZoneSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); ZoneSearch.groupBy(ZoneSearch.entity().getHypervisorType()); ZoneSearch.done(); - + AvailHyperSearch = createSearchBuilder(); AvailHyperSearch.and("zoneId", AvailHyperSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); AvailHyperSearch.select(null, Func.DISTINCT, AvailHyperSearch.entity().getHypervisorType()); AvailHyperSearch.done(); - - UUIDSearch = createSearchBuilder(); - UUIDSearch.and("uuid", UUIDSearch.entity().getUuid(), SearchCriteria.Op.EQ); - UUIDSearch.done(); - + + UUIDSearch = createSearchBuilder(); + UUIDSearch.and("uuid", UUIDSearch.entity().getUuid(), SearchCriteria.Op.EQ); + UUIDSearch.done(); + StateChangeSearch = createSearchBuilder(); StateChangeSearch.and("id", StateChangeSearch.entity().getId(), SearchCriteria.Op.EQ); StateChangeSearch.and("state", StateChangeSearch.entity().getState(), SearchCriteria.Op.EQ); StateChangeSearch.done(); } - + @Override public List listByZoneId(long zoneId) { SearchCriteria sc = ZoneSearch.create(); sc.setParameters("dataCenterId", zoneId); return listBy(sc); } - + @Override public List listByPodId(long podId) { SearchCriteria sc = PodSearch.create(); sc.setParameters("pod", podId); - + return listBy(sc); } - + @Override public ClusterVO findBy(String name, long podId) { SearchCriteria sc = PodSearch.create(); sc.setParameters("pod", podId); sc.setParameters("name", name); - + return findOneBy(sc); } - + @Override public List listByHyTypeWithoutGuid(String hyType) { SearchCriteria sc = HyTypeWithoutGuidSearch.create(); sc.setParameters("hypervisorType", hyType); - + return listBy(sc); } - + @Override public List listByDcHyType(long dcId, String hyType) { SearchCriteria sc = ZoneHyTypeSearch.create(); @@ -142,7 +140,7 @@ public class ClusterDaoImpl extends GenericDaoBase implements C sc.setParameters("hypervisorType", hyType); return listBy(sc); } - + @Override public List getAvailableHypervisorInZone(Long zoneId) { SearchCriteria sc = AvailHyperSearch.create(); @@ -154,13 +152,13 @@ public class ClusterDaoImpl extends GenericDaoBase implements C for (ClusterVO cluster : clusters) { hypers.add(cluster.getHypervisorType()); } - + return hypers; } - + @Override public Map> getPodClusterIdMap(List clusterIds){ - Transaction txn = Transaction.currentTxn(); + Transaction txn = Transaction.currentTxn(); PreparedStatement pstmt = null; Map> result = new HashMap>(); @@ -173,20 +171,20 @@ public class ClusterDaoImpl extends GenericDaoBase implements C sql.delete(sql.length()-1, sql.length()); sql.append(GET_POD_CLUSTER_MAP_SUFFIX); } - + pstmt = txn.prepareAutoCloseStatement(sql.toString()); ResultSet rs = pstmt.executeQuery(); while (rs.next()) { - Long podId = rs.getLong(1); - Long clusterIdInPod = rs.getLong(2); + Long podId = rs.getLong(1); + Long clusterIdInPod = rs.getLong(2); if(result.containsKey(podId)){ - List clusterList = result.get(podId); - clusterList.add(clusterIdInPod); - result.put(podId, clusterList); + List clusterList = result.get(podId); + clusterList.add(clusterIdInPod); + result.put(podId, clusterList); }else{ - List clusterList = new ArrayList(); - clusterList.add(clusterIdInPod); - result.put(podId, clusterList); + List clusterList = new ArrayList(); + clusterList.add(clusterIdInPod); + result.put(podId, clusterList); } } return result; @@ -196,49 +194,49 @@ public class ClusterDaoImpl extends GenericDaoBase implements C throw new CloudRuntimeException("Caught: " + GET_POD_CLUSTER_MAP_PREFIX, e); } } - + @Override public List listDisabledClusters(long zoneId, Long podId) { - GenericSearchBuilder clusterIdSearch = createSearchBuilder(Long.class); - clusterIdSearch.selectField(clusterIdSearch.entity().getId()); - clusterIdSearch.and("dataCenterId", clusterIdSearch.entity().getDataCenterId(), Op.EQ); - if(podId != null){ - clusterIdSearch.and("podId", clusterIdSearch.entity().getPodId(), Op.EQ); - } - clusterIdSearch.and("allocationState", clusterIdSearch.entity().getAllocationState(), Op.EQ); - clusterIdSearch.done(); + GenericSearchBuilder clusterIdSearch = createSearchBuilder(Long.class); + clusterIdSearch.selectField(clusterIdSearch.entity().getId()); + clusterIdSearch.and("dataCenterId", clusterIdSearch.entity().getDataCenterId(), Op.EQ); + if(podId != null){ + clusterIdSearch.and("podId", clusterIdSearch.entity().getPodId(), Op.EQ); + } + clusterIdSearch.and("allocationState", clusterIdSearch.entity().getAllocationState(), Op.EQ); + clusterIdSearch.done(); - - SearchCriteria sc = clusterIdSearch.create(); + + SearchCriteria sc = clusterIdSearch.create(); sc.addAnd("dataCenterId", SearchCriteria.Op.EQ, zoneId); if (podId != null) { - sc.addAnd("podId", SearchCriteria.Op.EQ, podId); - } + sc.addAnd("podId", SearchCriteria.Op.EQ, podId); + } sc.addAnd("allocationState", SearchCriteria.Op.EQ, Grouping.AllocationState.Disabled); return customSearch(sc, null); } @Override public List listClustersWithDisabledPods(long zoneId) { - - GenericSearchBuilder disabledPodIdSearch = _hostPodDao.createSearchBuilder(Long.class); - disabledPodIdSearch.selectField(disabledPodIdSearch.entity().getId()); - disabledPodIdSearch.and("dataCenterId", disabledPodIdSearch.entity().getDataCenterId(), Op.EQ); - disabledPodIdSearch.and("allocationState", disabledPodIdSearch.entity().getAllocationState(), Op.EQ); - GenericSearchBuilder clusterIdSearch = createSearchBuilder(Long.class); - clusterIdSearch.selectField(clusterIdSearch.entity().getId()); - clusterIdSearch.join("disabledPodIdSearch", disabledPodIdSearch, clusterIdSearch.entity().getPodId(), disabledPodIdSearch.entity().getId(), JoinBuilder.JoinType.INNER); - clusterIdSearch.done(); + GenericSearchBuilder disabledPodIdSearch = _hostPodDao.createSearchBuilder(Long.class); + disabledPodIdSearch.selectField(disabledPodIdSearch.entity().getId()); + disabledPodIdSearch.and("dataCenterId", disabledPodIdSearch.entity().getDataCenterId(), Op.EQ); + disabledPodIdSearch.and("allocationState", disabledPodIdSearch.entity().getAllocationState(), Op.EQ); - - SearchCriteria sc = clusterIdSearch.create(); + GenericSearchBuilder clusterIdSearch = createSearchBuilder(Long.class); + clusterIdSearch.selectField(clusterIdSearch.entity().getId()); + clusterIdSearch.join("disabledPodIdSearch", disabledPodIdSearch, clusterIdSearch.entity().getPodId(), disabledPodIdSearch.entity().getId(), JoinBuilder.JoinType.INNER); + clusterIdSearch.done(); + + + SearchCriteria sc = clusterIdSearch.create(); sc.setJoinParameters("disabledPodIdSearch", "dataCenterId", zoneId); sc.setJoinParameters("disabledPodIdSearch", "allocationState", Grouping.AllocationState.Disabled); - + return customSearch(sc, null); } - + @Override public boolean remove(Long id) { Transaction txn = Transaction.currentTxn(); @@ -246,30 +244,30 @@ public class ClusterDaoImpl extends GenericDaoBase implements C ClusterVO cluster = createForUpdate(); cluster.setName(null); cluster.setGuid(null); - + update(id, cluster); boolean result = super.remove(id); txn.commit(); return result; } - - - @Override - public ClusterVO findByUUID(String uuid) { - SearchCriteria sc = UUIDSearch.create(); - sc.setParameters("uuid", uuid); + + + @Override + public ClusterVO findByUUID(String uuid) { + SearchCriteria sc = UUIDSearch.create(); + sc.setParameters("uuid", uuid); return findOneBy(sc); - } + } - @Override - public boolean updateState(State currentState, Event event, State nextState, DataCenterResourceEntity clusterEntity, Object data) { - - ClusterVO vo = findById(clusterEntity.getId()); - - Date oldUpdatedTime = vo.getLastUpdated(); + @Override + public boolean updateState(State currentState, Event event, State nextState, DataCenterResourceEntity clusterEntity, Object data) { - SearchCriteria sc = StateChangeSearch.create(); + ClusterVO vo = findById(clusterEntity.getId()); + + Date oldUpdatedTime = vo.getLastUpdated(); + + SearchCriteria sc = StateChangeSearch.create(); sc.setParameters("id", vo.getId()); sc.setParameters("state", currentState); @@ -277,14 +275,14 @@ public class ClusterDaoImpl extends GenericDaoBase implements C builder.set(vo, "state", nextState); builder.set(vo, "lastUpdated", new Date()); - int rows = update((ClusterVO) vo, sc); - + int rows = update(vo, sc); + if (rows == 0 && s_logger.isDebugEnabled()) { - ClusterVO dbCluster = findByIdIncludingRemoved(vo.getId()); + ClusterVO dbCluster = findByIdIncludingRemoved(vo.getId()); if (dbCluster != null) { StringBuilder str = new StringBuilder("Unable to update ").append(vo.toString()); str.append(": DB Data={id=").append(dbCluster.getId()).append("; state=").append(dbCluster.getState()).append(";updatedTime=") - .append(dbCluster.getLastUpdated()); + .append(dbCluster.getLastUpdated()); str.append(": New Data={id=").append(vo.getId()).append("; state=").append(nextState).append("; event=").append(event).append("; updatedTime=").append(vo.getLastUpdated()); str.append(": stale Data={id=").append(vo.getId()).append("; state=").append(currentState).append("; event=").append(event).append("; updatedTime=").append(oldUpdatedTime); } else { @@ -292,7 +290,7 @@ public class ClusterDaoImpl extends GenericDaoBase implements C } } return rows > 0; - - } - + + } + } diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/DataCenterDaoImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/DataCenterDaoImpl.java index 61a4bb3e4b1..3a0d2c89a0d 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/DataCenterDaoImpl.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/DataCenterDaoImpl.java @@ -25,15 +25,12 @@ import javax.persistence.TableGenerator; import org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity; import org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity.State; import org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity.State.Event; -import org.apache.cloudstack.engine.datacenter.entity.api.ZoneEntity; import org.apache.cloudstack.engine.datacenter.entity.api.db.DataCenterVO; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.org.Grouping; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; @@ -63,47 +60,47 @@ public class DataCenterDaoImpl extends GenericDaoBase implem protected SearchBuilder TokenSearch; protected SearchBuilder StateChangeSearch; protected SearchBuilder UUIDSearch; - + protected long _prefix; protected Random _rand = new Random(System.currentTimeMillis()); protected TableGenerator _tgMacAddress; - + @Inject protected DcDetailsDao _detailsDao; @Override public DataCenterVO findByName(String name) { - SearchCriteria sc = NameSearch.create(); - sc.setParameters("name", name); + SearchCriteria sc = NameSearch.create(); + sc.setParameters("name", name); return findOneBy(sc); } @Override public DataCenterVO findByUUID(String uuid) { - SearchCriteria sc = UUIDSearch.create(); - sc.setParameters("uuid", uuid); + SearchCriteria sc = UUIDSearch.create(); + sc.setParameters("uuid", uuid); return findOneBy(sc); } - + @Override public DataCenterVO findByToken(String zoneToken){ - SearchCriteria sc = TokenSearch.create(); - sc.setParameters("zoneToken", zoneToken); + SearchCriteria sc = TokenSearch.create(); + sc.setParameters("zoneToken", zoneToken); return findOneBy(sc); } - + @Override public List findZonesByDomainId(Long domainId){ - SearchCriteria sc = ListZonesByDomainIdSearch.create(); - sc.setParameters("domainId", domainId); + SearchCriteria sc = ListZonesByDomainIdSearch.create(); + sc.setParameters("domainId", domainId); return listBy(sc); } - + @Override public List findZonesByDomainId(Long domainId, String keyword){ - SearchCriteria sc = ListZonesByDomainIdSearch.create(); - sc.setParameters("domainId", domainId); - if (keyword != null) { + SearchCriteria sc = ListZonesByDomainIdSearch.create(); + sc.setParameters("domainId", domainId); + if (keyword != null) { SearchCriteria ssc = createSearchCriteria(); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); ssc.addOr("description", SearchCriteria.Op.LIKE, "%" + keyword + "%"); @@ -111,12 +108,12 @@ public class DataCenterDaoImpl extends GenericDaoBase implem } return listBy(sc); } - + @Override public List findChildZones(Object[] ids, String keyword){ - SearchCriteria sc = ChildZonesSearch.create(); - sc.setParameters("domainid", ids); - if (keyword != null) { + SearchCriteria sc = ChildZonesSearch.create(); + sc.setParameters("domainid", ids); + if (keyword != null) { SearchCriteria ssc = createSearchCriteria(); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); ssc.addOr("description", SearchCriteria.Op.LIKE, "%" + keyword + "%"); @@ -124,28 +121,28 @@ public class DataCenterDaoImpl extends GenericDaoBase implem } return listBy(sc); } - + @Override public List listPublicZones(String keyword){ - SearchCriteria sc = PublicZonesSearch.create(); - if (keyword != null) { + SearchCriteria sc = PublicZonesSearch.create(); + if (keyword != null) { SearchCriteria ssc = createSearchCriteria(); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); ssc.addOr("description", SearchCriteria.Op.LIKE, "%" + keyword + "%"); sc.addAnd("name", SearchCriteria.Op.SC, ssc); } - //sc.setParameters("domainId", domainId); + //sc.setParameters("domainId", domainId); return listBy(sc); } - + @Override public List findByKeyword(String keyword){ - SearchCriteria ssc = createSearchCriteria(); - ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - ssc.addOr("description", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + SearchCriteria ssc = createSearchCriteria(); + ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("description", SearchCriteria.Op.LIKE, "%" + keyword + "%"); return listBy(ssc); } - + @Override public String[] getNextAvailableMacAddressPair(long id) { @@ -155,7 +152,7 @@ public class DataCenterDaoImpl extends GenericDaoBase implem @Override public String[] getNextAvailableMacAddressPair(long id, long mask) { SequenceFetcher fetch = SequenceFetcher.getInstance(); - + long seq = fetch.getNextSequence(Long.class, _tgMacAddress, id); seq = seq | _prefix | ((id & 0x7f) << 32); seq |= mask; @@ -172,49 +169,49 @@ public class DataCenterDaoImpl extends GenericDaoBase implem if (!super.configure(name, params)) { return false; } - + String value = (String)params.get("mac.address.prefix"); _prefix = (long)NumbersUtil.parseInt(value, 06) << 40; return true; } - + protected DataCenterDaoImpl() { super(); NameSearch = createSearchBuilder(); NameSearch.and("name", NameSearch.entity().getName(), SearchCriteria.Op.EQ); NameSearch.done(); - + ListZonesByDomainIdSearch = createSearchBuilder(); ListZonesByDomainIdSearch.and("domainId", ListZonesByDomainIdSearch.entity().getDomainId(), SearchCriteria.Op.EQ); ListZonesByDomainIdSearch.done(); - + PublicZonesSearch = createSearchBuilder(); PublicZonesSearch.and("domainId", PublicZonesSearch.entity().getDomainId(), SearchCriteria.Op.NULL); PublicZonesSearch.done(); - + ChildZonesSearch = createSearchBuilder(); ChildZonesSearch.and("domainid", ChildZonesSearch.entity().getDomainId(), SearchCriteria.Op.IN); ChildZonesSearch.done(); - + DisabledZonesSearch = createSearchBuilder(); DisabledZonesSearch.and("allocationState", DisabledZonesSearch.entity().getAllocationState(), SearchCriteria.Op.EQ); DisabledZonesSearch.done(); - + TokenSearch = createSearchBuilder(); TokenSearch.and("zoneToken", TokenSearch.entity().getZoneToken(), SearchCriteria.Op.EQ); TokenSearch.done(); - + StateChangeSearch = createSearchBuilder(); StateChangeSearch.and("id", StateChangeSearch.entity().getId(), SearchCriteria.Op.EQ); StateChangeSearch.and("state", StateChangeSearch.entity().getState(), SearchCriteria.Op.EQ); StateChangeSearch.done(); - + UUIDSearch = createSearchBuilder(); UUIDSearch.and("uuid", UUIDSearch.entity().getUuid(), SearchCriteria.Op.EQ); UUIDSearch.done(); - + _tgMacAddress = _tgs.get("macAddress"); assert _tgMacAddress != null : "Couldn't get mac address table generator"; } @@ -231,7 +228,7 @@ public class DataCenterDaoImpl extends GenericDaoBase implem txn.commit(); return persisted; } - + @Override public void loadDetails(DataCenterVO zone) { Map details =_detailsDao.findDetails(zone.getId()); @@ -246,25 +243,25 @@ public class DataCenterDaoImpl extends GenericDaoBase implem } _detailsDao.persist(zone.getId(), details); } - + @Override public List listDisabledZones(){ - SearchCriteria sc = DisabledZonesSearch.create(); - sc.setParameters("allocationState", Grouping.AllocationState.Disabled); - - List dcs = listBy(sc); - - return dcs; + SearchCriteria sc = DisabledZonesSearch.create(); + sc.setParameters("allocationState", Grouping.AllocationState.Disabled); + + List dcs = listBy(sc); + + return dcs; } - + @Override public List listEnabledZones(){ - SearchCriteria sc = DisabledZonesSearch.create(); - sc.setParameters("allocationState", Grouping.AllocationState.Enabled); - - List dcs = listBy(sc); - - return dcs; + SearchCriteria sc = DisabledZonesSearch.create(); + sc.setParameters("allocationState", Grouping.AllocationState.Enabled); + + List dcs = listBy(sc); + + return dcs; } @Override @@ -277,36 +274,36 @@ public class DataCenterDaoImpl extends GenericDaoBase implem Long dcId = Long.parseLong(tokenOrIdOrName); return findById(dcId); } catch (NumberFormatException nfe) { - + } } } return result; } - + @Override public boolean remove(Long id) { Transaction txn = Transaction.currentTxn(); txn.start(); DataCenterVO zone = createForUpdate(); zone.setName(null); - + update(id, zone); boolean result = super.remove(id); txn.commit(); return result; } - - @Override - public boolean updateState(State currentState, Event event, State nextState, DataCenterResourceEntity zoneEntity, Object data) { - - DataCenterVO vo = findById(zoneEntity.getId()); - - Date oldUpdatedTime = vo.getLastUpdated(); - SearchCriteria sc = StateChangeSearch.create(); + @Override + public boolean updateState(State currentState, Event event, State nextState, DataCenterResourceEntity zoneEntity, Object data) { + + DataCenterVO vo = findById(zoneEntity.getId()); + + Date oldUpdatedTime = vo.getLastUpdated(); + + SearchCriteria sc = StateChangeSearch.create(); sc.setParameters("id", vo.getId()); sc.setParameters("state", currentState); @@ -314,14 +311,14 @@ public class DataCenterDaoImpl extends GenericDaoBase implem builder.set(vo, "state", nextState); builder.set(vo, "lastUpdated", new Date()); - int rows = update((DataCenterVO) vo, sc); - + int rows = update(vo, sc); + if (rows == 0 && s_logger.isDebugEnabled()) { - DataCenterVO dbDC = findByIdIncludingRemoved(vo.getId()); + DataCenterVO dbDC = findByIdIncludingRemoved(vo.getId()); if (dbDC != null) { StringBuilder str = new StringBuilder("Unable to update ").append(vo.toString()); str.append(": DB Data={id=").append(dbDC.getId()).append("; state=").append(dbDC.getState()).append(";updatedTime=") - .append(dbDC.getLastUpdated()); + .append(dbDC.getLastUpdated()); str.append(": New Data={id=").append(vo.getId()).append("; state=").append(nextState).append("; event=").append(event).append("; updatedTime=").append(vo.getLastUpdated()); str.append(": stale Data={id=").append(vo.getId()).append("; state=").append(currentState).append("; event=").append(event).append("; updatedTime=").append(oldUpdatedTime); } else { @@ -329,8 +326,8 @@ public class DataCenterDaoImpl extends GenericDaoBase implem } } return rows > 0; - - } - - + + } + + } diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/HostDaoImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/HostDaoImpl.java index 41e6785a95a..f33bc21256e 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/HostDaoImpl.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/dao/HostDaoImpl.java @@ -31,8 +31,6 @@ import javax.persistence.TableGenerator; import org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity; import org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity.State; -import org.apache.cloudstack.engine.datacenter.entity.api.db.ClusterVO; -import org.apache.cloudstack.engine.datacenter.entity.api.db.DataCenterVO; import org.apache.cloudstack.engine.datacenter.entity.api.db.HostVO; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -40,21 +38,17 @@ import org.springframework.stereotype.Component; import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.host.HostTagVO; - import com.cloud.host.Status; -import com.cloud.host.Status.Event; import com.cloud.info.RunningHostCountInfo; import com.cloud.org.Managed; import com.cloud.resource.ResourceState; import com.cloud.utils.DateUtil; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.Attribute; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; -import com.cloud.utils.db.JoinBuilder.JoinType; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; @@ -104,7 +98,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao protected final SearchBuilder ManagedRoutingServersSearch; protected final SearchBuilder SecondaryStorageVMSearch; protected SearchBuilder StateChangeSearch; - + protected SearchBuilder UUIDSearch; protected final GenericSearchBuilder HostsInStatusSearch; @@ -119,7 +113,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao @Inject protected HostDetailsDao _detailsDao; @Inject protected HostTagsDao _hostTagsDao; @Inject protected ClusterDao _clusterDao; - + public HostDaoImpl() { @@ -148,7 +142,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao TypeDcSearch.and("type", TypeDcSearch.entity().getType(), SearchCriteria.Op.EQ); TypeDcSearch.and("dc", TypeDcSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); TypeDcSearch.done(); - + SecondaryStorageVMSearch = createSearchBuilder(); SecondaryStorageVMSearch.and("type", SecondaryStorageVMSearch.entity().getType(), SearchCriteria.Op.EQ); SecondaryStorageVMSearch.and("dc", SecondaryStorageVMSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); @@ -161,14 +155,14 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao TypeDcStatusSearch.and("status", TypeDcStatusSearch.entity().getStatus(), SearchCriteria.Op.EQ); TypeDcStatusSearch.and("resourceState", TypeDcStatusSearch.entity().getResourceState(), SearchCriteria.Op.EQ); TypeDcStatusSearch.done(); - + TypeClusterStatusSearch = createSearchBuilder(); TypeClusterStatusSearch.and("type", TypeClusterStatusSearch.entity().getType(), SearchCriteria.Op.EQ); TypeClusterStatusSearch.and("cluster", TypeClusterStatusSearch.entity().getClusterId(), SearchCriteria.Op.EQ); TypeClusterStatusSearch.and("status", TypeClusterStatusSearch.entity().getStatus(), SearchCriteria.Op.EQ); TypeClusterStatusSearch.and("resourceState", TypeClusterStatusSearch.entity().getResourceState(), SearchCriteria.Op.EQ); TypeClusterStatusSearch.done(); - + IdStatusSearch = createSearchBuilder(); IdStatusSearch.and("id", IdStatusSearch.entity().getId(), SearchCriteria.Op.EQ); IdStatusSearch.and("states", IdStatusSearch.entity().getStatus(), SearchCriteria.Op.IN); @@ -214,7 +208,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao StatusSearch = createSearchBuilder(); StatusSearch.and("status", StatusSearch.entity().getStatus(), SearchCriteria.Op.IN); StatusSearch.done(); - + ResourceStateSearch = createSearchBuilder(); ResourceStateSearch.and("resourceState", ResourceStateSearch.entity().getResourceState(), SearchCriteria.Op.IN); ResourceStateSearch.done(); @@ -299,7 +293,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao ManagedRoutingServersSearch.and("server", ManagedRoutingServersSearch.entity().getManagementServerId(), SearchCriteria.Op.NNULL); ManagedRoutingServersSearch.and("type", ManagedRoutingServersSearch.entity().getType(), SearchCriteria.Op.EQ); ManagedRoutingServersSearch.done(); - + RoutingSearch = createSearchBuilder(); RoutingSearch.and("type", RoutingSearch.entity().getType(), SearchCriteria.Op.EQ); RoutingSearch.done(); @@ -310,11 +304,11 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao _resourceStateAttr = _allAttributes.get("resourceState"); assert (_statusAttr != null && _msIdAttr != null && _pingTimeAttr != null) : "Couldn't find one of these attributes"; - - UUIDSearch = createSearchBuilder(); - UUIDSearch.and("uuid", UUIDSearch.entity().getUuid(), SearchCriteria.Op.EQ); - UUIDSearch.done(); - + + UUIDSearch = createSearchBuilder(); + UUIDSearch.and("uuid", UUIDSearch.entity().getUuid(), SearchCriteria.Op.EQ); + UUIDSearch.done(); + StateChangeSearch = createSearchBuilder(); StateChangeSearch.and("id", StateChangeSearch.entity().getId(), SearchCriteria.Op.EQ); StateChangeSearch.and("state", StateChangeSearch.entity().getState(), SearchCriteria.Op.EQ); @@ -331,52 +325,52 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao List hosts = listBy(sc); return hosts.size(); } - + @Override public HostVO findByGuid(String guid) { SearchCriteria sc = GuidSearch.create("guid", guid); return findOneBy(sc); } - + @Override @DB public List findAndUpdateDirectAgentToLoad(long lastPingSecondsAfter, Long limit, long managementServerId) { Transaction txn = Transaction.currentTxn(); txn.start(); - SearchCriteria sc = UnmanagedDirectConnectSearch.create(); - sc.setParameters("lastPinged", lastPingSecondsAfter); + SearchCriteria sc = UnmanagedDirectConnectSearch.create(); + sc.setParameters("lastPinged", lastPingSecondsAfter); //sc.setParameters("resourceStates", ResourceState.ErrorInMaintenance, ResourceState.Maintenance, ResourceState.PrepareForMaintenance, ResourceState.Disabled); sc.setJoinParameters("ClusterManagedSearch", "managed", Managed.ManagedState.Managed); List hosts = lockRows(sc, new Filter(HostVO.class, "clusterId", true, 0L, limit), true); - + for (HostVO host : hosts) { host.setManagementServerId(managementServerId); update(host.getId(), host); } - + txn.commit(); - + return hosts; } - + @Override @DB public List findAndUpdateApplianceToLoad(long lastPingSecondsAfter, long managementServerId) { - Transaction txn = Transaction.currentTxn(); - - txn.start(); - SearchCriteria sc = UnmanagedApplianceSearch.create(); - sc.setParameters("lastPinged", lastPingSecondsAfter); + Transaction txn = Transaction.currentTxn(); + + txn.start(); + SearchCriteria sc = UnmanagedApplianceSearch.create(); + sc.setParameters("lastPinged", lastPingSecondsAfter); sc.setParameters("types", Type.ExternalDhcp, Type.ExternalFirewall, Type.ExternalLoadBalancer, Type.PxeServer, Type.TrafficMonitor, Type.L2Networking); - List hosts = lockRows(sc, null, true); - - for (HostVO host : hosts) { - host.setManagementServerId(managementServerId); - update(host.getId(), host); - } - - txn.commit(); - - return hosts; + List hosts = lockRows(sc, null, true); + + for (HostVO host : hosts) { + host.setManagementServerId(managementServerId); + update(host.getId(), host); + } + + txn.commit(); + + return hosts; } @Override @@ -402,7 +396,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao ub = getUpdateBuilder(host); update(ub, sc, null); } - + @Override public List listByHostTag(Host.Type type, Long clusterId, Long podId, long dcId, String hostTag) { @@ -435,8 +429,8 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao return listBy(sc); } - - + + @Override public List listAllUpAndEnabledNonHAHosts(Type type, Long clusterId, Long podId, long dcId, String haTag) { SearchBuilder hostTagSearch = null; @@ -446,42 +440,42 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao hostTagSearch.or("tagNull", hostTagSearch.entity().getTag(), SearchCriteria.Op.NULL); hostTagSearch.cp(); } - + SearchBuilder hostSearch = createSearchBuilder(); - + hostSearch.and("type", hostSearch.entity().getType(), SearchCriteria.Op.EQ); hostSearch.and("clusterId", hostSearch.entity().getClusterId(), SearchCriteria.Op.EQ); hostSearch.and("podId", hostSearch.entity().getPodId(), SearchCriteria.Op.EQ); hostSearch.and("zoneId", hostSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); hostSearch.and("status", hostSearch.entity().getStatus(), SearchCriteria.Op.EQ); hostSearch.and("resourceState", hostSearch.entity().getResourceState(), SearchCriteria.Op.EQ); - + if (haTag != null && !haTag.isEmpty()) { hostSearch.join("hostTagSearch", hostTagSearch, hostSearch.entity().getId(), hostTagSearch.entity().getHostId(), JoinBuilder.JoinType.LEFTOUTER); } SearchCriteria sc = hostSearch.create(); - + if (haTag != null && !haTag.isEmpty()) { sc.setJoinParameters("hostTagSearch", "tag", haTag); } - + if (type != null) { sc.setParameters("type", type); } - + if (clusterId != null) { sc.setParameters("clusterId", clusterId); } - + if (podId != null) { sc.setParameters("podId", podId); } - + sc.setParameters("zoneId", dcId); sc.setParameters("status", Status.Up); sc.setParameters("resourceState", ResourceState.Enabled); - + return listBy(sc); } @@ -528,7 +522,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao } return result; } - + @Override public void saveDetails(HostVO host) { Map details = host.getDetails(); @@ -650,12 +644,12 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao } - @Override - public boolean updateState(State currentState, DataCenterResourceEntity.State.Event event, State nextState, DataCenterResourceEntity hostEntity, Object data) { - HostVO vo = findById(hostEntity.getId()); - Date oldUpdatedTime = vo.getLastUpdated(); + @Override + public boolean updateState(State currentState, DataCenterResourceEntity.State.Event event, State nextState, DataCenterResourceEntity hostEntity, Object data) { + HostVO vo = findById(hostEntity.getId()); + Date oldUpdatedTime = vo.getLastUpdated(); - SearchCriteria sc = StateChangeSearch.create(); + SearchCriteria sc = StateChangeSearch.create(); sc.setParameters("id", hostEntity.getId()); sc.setParameters("state", currentState); @@ -663,14 +657,14 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao builder.set(vo, "state", nextState); builder.set(vo, "lastUpdated", new Date()); - int rows = update((HostVO) vo, sc); - + int rows = update(vo, sc); + if (rows == 0 && s_logger.isDebugEnabled()) { - HostVO dbHost = findByIdIncludingRemoved(vo.getId()); + HostVO dbHost = findByIdIncludingRemoved(vo.getId()); if (dbHost != null) { StringBuilder str = new StringBuilder("Unable to update ").append(vo.toString()); str.append(": DB Data={id=").append(dbHost.getId()).append("; state=").append(dbHost.getState()).append(";updatedTime=") - .append(dbHost.getLastUpdated()); + .append(dbHost.getLastUpdated()); str.append(": New Data={id=").append(vo.getId()).append("; state=").append(nextState).append("; event=").append(event).append("; updatedTime=").append(vo.getLastUpdated()); str.append(": stale Data={id=").append(vo.getId()).append("; state=").append(currentState).append("; event=").append(event).append("; updatedTime=").append(oldUpdatedTime); } else { @@ -678,8 +672,8 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao } } return rows > 0; - } - + } + @Override public boolean updateResourceState(ResourceState oldState, ResourceState.Event event, ResourceState newState, Host vo) { HostVO host = (HostVO)vo; @@ -687,41 +681,41 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao sb.and("resource_state", sb.entity().getResourceState(), SearchCriteria.Op.EQ); sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); sb.done(); - + SearchCriteria sc = sb.create(); sc.setParameters("resource_state", oldState); sc.setParameters("id", host.getId()); - + UpdateBuilder ub = getUpdateBuilder(host); ub.set(host, _resourceStateAttr, newState); int result = update(ub, sc, null); assert result <= 1 : "How can this update " + result + " rows? "; - + if (state_logger.isDebugEnabled() && result == 0) { HostVO ho = findById(host.getId()); assert ho != null : "How how how? : " + host.getId(); StringBuilder str = new StringBuilder("Unable to update resource state: ["); - str.append("m = " + host.getId()); - str.append("; name = " + host.getName()); - str.append("; old state = " + oldState); - str.append("; event = " + event); - str.append("; new state = " + newState + "]"); - state_logger.debug(str.toString()); + str.append("m = " + host.getId()); + str.append("; name = " + host.getName()); + str.append("; old state = " + oldState); + str.append("; event = " + event); + str.append("; new state = " + newState + "]"); + state_logger.debug(str.toString()); } else { - StringBuilder msg = new StringBuilder("Resource state update: ["); - msg.append("id = " + host.getId()); - msg.append("; name = " + host.getName()); - msg.append("; old state = " + oldState); - msg.append("; event = " + event); - msg.append("; new state = " + newState + "]"); - state_logger.debug(msg.toString()); + StringBuilder msg = new StringBuilder("Resource state update: ["); + msg.append("id = " + host.getId()); + msg.append("; name = " + host.getName()); + msg.append("; old state = " + oldState); + msg.append("; event = " + event); + msg.append("; new state = " + newState + "]"); + state_logger.debug(msg.toString()); } - + return result > 0; } - + @Override public HostVO findByTypeNameAndZoneId(long zoneId, String name, Host.Type type) { SearchCriteria sc = TypeNameZoneSearch.create(); @@ -731,94 +725,94 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao return findOneBy(sc); } - @Override - public List findHypervisorHostInCluster(long clusterId) { - SearchCriteria sc = TypeClusterStatusSearch.create(); - sc.setParameters("type", Host.Type.Routing); - sc.setParameters("cluster", clusterId); - sc.setParameters("status", Status.Up); - sc.setParameters("resourceState", ResourceState.Enabled); - - return listBy(sc); - } + @Override + public List findHypervisorHostInCluster(long clusterId) { + SearchCriteria sc = TypeClusterStatusSearch.create(); + sc.setParameters("type", Host.Type.Routing); + sc.setParameters("cluster", clusterId); + sc.setParameters("status", Status.Up); + sc.setParameters("resourceState", ResourceState.Enabled); - @Override - public List lockRows( - SearchCriteria sc, - Filter filter, boolean exclusive) { - // TODO Auto-generated method stub - return null; - } + return listBy(sc); + } - @Override - public org.apache.cloudstack.engine.datacenter.entity.api.db.HostVO lockOneRandomRow( - SearchCriteria sc, - boolean exclusive) { - // TODO Auto-generated method stub - return null; - } + @Override + public List lockRows( + SearchCriteria sc, + Filter filter, boolean exclusive) { + // TODO Auto-generated method stub + return null; + } + + @Override + public org.apache.cloudstack.engine.datacenter.entity.api.db.HostVO lockOneRandomRow( + SearchCriteria sc, + boolean exclusive) { + // TODO Auto-generated method stub + return null; + } - @Override - public List search( - SearchCriteria sc, - Filter filter) { - // TODO Auto-generated method stub - return null; - } + @Override + public List search( + SearchCriteria sc, + Filter filter) { + // TODO Auto-generated method stub + return null; + } - @Override - public List search( - SearchCriteria sc, - Filter filter, boolean enable_query_cache) { - // TODO Auto-generated method stub - return null; - } + @Override + public List search( + SearchCriteria sc, + Filter filter, boolean enable_query_cache) { + // TODO Auto-generated method stub + return null; + } - @Override - public List searchIncludingRemoved( - SearchCriteria sc, - Filter filter, Boolean lock, boolean cache) { - // TODO Auto-generated method stub - return null; - } + @Override + public List searchIncludingRemoved( + SearchCriteria sc, + Filter filter, Boolean lock, boolean cache) { + // TODO Auto-generated method stub + return null; + } - @Override - public List searchIncludingRemoved( - SearchCriteria sc, - Filter filter, Boolean lock, boolean cache, - boolean enable_query_cache) { - // TODO Auto-generated method stub - return null; - } + @Override + public List searchIncludingRemoved( + SearchCriteria sc, + Filter filter, Boolean lock, boolean cache, + boolean enable_query_cache) { + // TODO Auto-generated method stub + return null; + } - @Override - public int remove( - SearchCriteria sc) { - // TODO Auto-generated method stub - return 0; - } + @Override + public int remove( + SearchCriteria sc) { + // TODO Auto-generated method stub + return 0; + } - @Override - public int expunge(SearchCriteria sc) { - // TODO Auto-generated method stub - return 0; - } + @Override + public int expunge(SearchCriteria sc) { + // TODO Auto-generated method stub + return 0; + } - @Override - public HostVO findOneBy(SearchCriteria sc) { - // TODO Auto-generated method stub - return null; - } + @Override + public HostVO findOneBy(SearchCriteria sc) { + // TODO Auto-generated method stub + return null; + } - @Override - public HostVO findByUUID(String uuid) { - SearchCriteria sc = UUIDSearch.create(); - sc.setParameters("uuid", uuid); + @Override + public HostVO findByUUID(String uuid) { + SearchCriteria sc = UUIDSearch.create(); + sc.setParameters("uuid", uuid); return findOneBy(sc); - } + } } diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/provider/DefaultImageDataStoreProvider.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/provider/DefaultImageDataStoreProvider.java index f87299da9b8..363ed5f2ff2 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/provider/DefaultImageDataStoreProvider.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/provider/DefaultImageDataStoreProvider.java @@ -18,8 +18,6 @@ */ package org.apache.cloudstack.storage.image.provider; -import java.util.Map; - import javax.inject.Inject; import org.apache.cloudstack.storage.image.db.ImageDataStoreDao; @@ -34,7 +32,7 @@ import org.apache.cloudstack.storage.image.store.lifecycle.DefaultImageDataStore import org.apache.cloudstack.storage.image.store.lifecycle.ImageDataStoreLifeCycle; import org.springframework.stereotype.Component; -import com.cloud.utils.component.ComponentInject; +import com.cloud.utils.component.ComponentContext; @Component public class DefaultImageDataStoreProvider implements ImageDataStoreProvider { @@ -50,7 +48,7 @@ public class DefaultImageDataStoreProvider implements ImageDataStoreProvider { ImageDataStoreVO idsv = imageStoreDao.findById(imageStoreId); ImageDataStoreDriver driver = new ImageDataStoreDriverImpl(); ImageDataStore ids = new ImageDataStoreImpl(idsv, driver, false); - ids = ComponentInject.inject(ids); + ids = ComponentContext.inject(ids); return ids; } @@ -72,6 +70,6 @@ public class DefaultImageDataStoreProvider implements ImageDataStoreProvider { @Override public ImageDataStoreLifeCycle getLifeCycle() { - return new DefaultImageDataStoreLifeCycle(this, provider, imageStoreDao); + return new DefaultImageDataStoreLifeCycle(this, provider, imageStoreDao); } } diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/DefaultPrimaryDataStore.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/DefaultPrimaryDataStore.java index 571f227667a..ddcb3652f7c 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/DefaultPrimaryDataStore.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/DefaultPrimaryDataStore.java @@ -6,7 +6,6 @@ import java.util.List; import javax.inject.Inject; import org.apache.cloudstack.engine.datacenter.entity.api.DataCenterResourceEntity; -import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreProvider; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; @@ -27,13 +26,12 @@ import org.apache.cloudstack.storage.volume.TemplatePrimaryDataStoreManager; import org.apache.cloudstack.storage.volume.VolumeObject; import org.apache.cloudstack.storage.volume.db.VolumeDao2; import org.apache.cloudstack.storage.volume.db.VolumeVO; - import org.apache.log4j.Logger; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.utils.component.ComponentInject; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CloudRuntimeException; import edu.emory.mathcs.backport.java.util.Collections; @@ -55,42 +53,42 @@ public class DefaultPrimaryDataStore implements PrimaryDataStore { private PrimaryDataStoreDao dataStoreDao; @Inject private TemplatePrimaryDataStoreManager templatePrimaryStoreMgr; - + private DefaultPrimaryDataStore(PrimaryDataStoreVO pdsv) { this.pdsv = pdsv; } - + public void setDriver(PrimaryDataStoreDriver driver) { driver.setDataStore(this); this.driver = driver; } - + public void setLifeCycle(PrimaryDataStoreLifeCycle lifeCycle) { lifeCycle.setDataStore(this); this.lifeCycle = lifeCycle; } - + public void setProvider(PrimaryDataStoreProvider provider) { this.provider = provider; } - + public void setProtocolTransFormer(StorageProtocolTransformer transformer) { this.protocalTransformer = transformer; } - + @Override public PrimaryDataStoreTO getDataStoreTO() { return this.protocalTransformer.getDataStoreTO(this); } - + @Override public VolumeTO getVolumeTO(VolumeInfo volume) { return this.protocalTransformer.getVolumeTO(volume); } - + public static DefaultPrimaryDataStore createDataStore(PrimaryDataStoreVO pdsv) { DefaultPrimaryDataStore dataStore = new DefaultPrimaryDataStore(pdsv); - return ComponentInject.inject(dataStore); + return ComponentContext.inject(dataStore); } @Override @@ -130,12 +128,12 @@ public class DefaultPrimaryDataStore implements PrimaryDataStore { return new ArrayList(); } } - + List endpoints = new ArrayList(); List hosts = hostDao.findHypervisorHostInCluster(clusterId); for (HostVO host : hosts) { HypervisorHostEndPoint ep = new HypervisorHostEndPoint(host.getId(), host.getPrivateIpAddress()); - ComponentInject.inject(ep); + ComponentContext.inject(ep); endpoints.add(ep); } Collections.shuffle(endpoints); @@ -145,12 +143,12 @@ public class DefaultPrimaryDataStore implements PrimaryDataStore { public void setSupportedHypervisor(HypervisorType type) { this.supportedHypervisor = type; } - + @Override public boolean isHypervisorSupported(HypervisorType hypervisor) { return (this.supportedHypervisor == hypervisor) ? true : false; } - + public void setLocalStorageFlag(boolean supported) { this.isLocalStorageSupported = supported; } @@ -167,7 +165,7 @@ public class DefaultPrimaryDataStore implements PrimaryDataStore { @Override public long getCapacity() { - return this.driver.getCapacity(); + return this.driver.getCapacity(); } @Override @@ -219,7 +217,7 @@ public class DefaultPrimaryDataStore implements PrimaryDataStore { //this.driver.createVolumeFromBaseImage(vo, template); return volume; } - + @Override public void createVoluemFromBaseImageAsync(VolumeInfo volume, TemplateOnPrimaryDataStoreInfo templateStore, AsyncCompletionCallback callback) { VolumeObject vo = (VolumeObject) volume; @@ -227,7 +225,7 @@ public class DefaultPrimaryDataStore implements PrimaryDataStore { this.driver.createVolumeFromBaseImageAsync(vo, templateStore, callback); } - + @Override public boolean installTemplate(TemplateOnPrimaryDataStoreInfo template) { // TODO Auto-generated method stub @@ -264,5 +262,5 @@ public class DefaultPrimaryDataStore implements PrimaryDataStore { return this.provider; } - + } diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/kvm/KvmNfsConfigurator.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/kvm/KvmNfsConfigurator.java index 6897e547729..1c36f152a97 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/kvm/KvmNfsConfigurator.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/kvm/KvmNfsConfigurator.java @@ -18,19 +18,21 @@ */ package org.apache.cloudstack.storage.datastore.configurator.kvm; +import javax.inject.Inject; + import org.apache.cloudstack.storage.datastore.configurator.validator.NfsProtocolTransformer; import org.apache.cloudstack.storage.datastore.configurator.validator.StorageProtocolTransformer; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; -import com.cloud.utils.component.Inject; @Component @Qualifier("defaultProvider") public class KvmNfsConfigurator extends AbstractKvmConfigurator { @Inject PrimaryDataStoreDao dataStoreDao; + @Override public String getSupportedDataStoreType() { return "nfs"; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/vmware/VmwareNfsConfigurator.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/vmware/VmwareNfsConfigurator.java index c6afc133e3a..afd8d21a626 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/vmware/VmwareNfsConfigurator.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/vmware/VmwareNfsConfigurator.java @@ -18,15 +18,14 @@ */ package org.apache.cloudstack.storage.datastore.configurator.vmware; +import javax.inject.Inject; + import org.apache.cloudstack.storage.datastore.configurator.validator.NfsProtocolTransformer; import org.apache.cloudstack.storage.datastore.configurator.validator.StorageProtocolTransformer; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; -import com.cloud.storage.Storage.StoragePoolType; -import com.cloud.utils.component.Inject; - @Component @Qualifier("defaultProvider") public class VmwareNfsConfigurator extends AbstractVmwareConfigurator { diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/xen/XenNfsConfigurator.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/xen/XenNfsConfigurator.java index 6ad4b536349..0cb24a8d574 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/xen/XenNfsConfigurator.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/xen/XenNfsConfigurator.java @@ -20,26 +20,22 @@ package org.apache.cloudstack.storage.datastore.configurator.xen; import org.apache.cloudstack.storage.datastore.configurator.validator.NfsProtocolTransformer; import org.apache.cloudstack.storage.datastore.configurator.validator.StorageProtocolTransformer; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.protocol.DataStoreProtocol; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; -import com.cloud.storage.Storage.StoragePoolType; -import com.cloud.utils.component.Inject; - @Component @Qualifier("defaultProvider") public class XenNfsConfigurator extends AbstractXenConfigurator { - @Override - public String getSupportedDataStoreType() { - return DataStoreProtocol.NFS.toString(); - } + @Override + public String getSupportedDataStoreType() { + return DataStoreProtocol.NFS.toString(); + } - @Override - public StorageProtocolTransformer getProtocolTransformer() { - return new NfsProtocolTransformer(dataStoreDao); - } + @Override + public StorageProtocolTransformer getProtocolTransformer() { + return new NfsProtocolTransformer(dataStoreDao); + } @Override protected boolean isLocalStorageSupported() { diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java index 247cdeec682..99cb00140ec 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java @@ -31,19 +31,14 @@ import javax.naming.ConfigurationException; import org.apache.cloudstack.storage.datastore.DataStoreStatus; import org.springframework.stereotype.Component; -import com.cloud.storage.StoragePoolDetailVO; -import com.cloud.storage.dao.StoragePoolDetailsDao; -import com.cloud.storage.dao.StoragePoolDetailsDaoImpl; -import com.cloud.utils.component.ComponentInject; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.Transaction; import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; @Component @@ -101,7 +96,7 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase getStateMachine() { return s_fsm; } + @Override public VolumeVO processEvent(Volume vol, Volume.Event event) throws NoTransitionException { // _volStateMachine.transitTo(vol, event, null, _volumeDao); return _volumeDao.findById(vol.getId()); } + @Override public VolumeProfile getProfile(long volumeId) { // TODO Auto-generated method stub return null; } + @Override public VolumeVO getVolume(long volumeId) { // TODO Auto-generated method stub return null; } + @Override public VolumeVO updateVolume(VolumeVO volume) { // TODO Auto-generated method stub return null; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java index a1eeb65b487..4ec6fba9497 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java @@ -13,13 +13,11 @@ import org.apache.cloudstack.engine.subsystem.api.storage.type.VolumeTypeHelper; import org.apache.cloudstack.storage.datastore.PrimaryDataStore; import org.apache.cloudstack.storage.volume.db.VolumeDao2; import org.apache.cloudstack.storage.volume.db.VolumeVO; - import org.apache.log4j.Logger; import com.cloud.storage.Volume; import com.cloud.storage.Volume.State; -import com.cloud.utils.component.ComponentInject; -import com.cloud.utils.db.DB; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.StateMachine2; @@ -41,13 +39,14 @@ public class VolumeObject implements VolumeInfo { this.volumeVO = volumeVO; this.dataStore = dataStore; } - + public static VolumeObject getVolumeObject(PrimaryDataStore dataStore, VolumeVO volumeVO) { VolumeObject vo = new VolumeObject(dataStore, volumeVO); - vo = ComponentInject.inject(vo); + vo = ComponentContext.inject(vo); return vo; } + @Override public String getUuid() { return volumeVO.getUuid(); } @@ -56,14 +55,17 @@ public class VolumeObject implements VolumeInfo { volumeVO.setUuid(uuid); } + @Override public String getPath() { return volumeVO.getPath(); } + @Override public String getTemplateUuid() { return null; } + @Override public String getTemplatePath() { return null; } @@ -76,18 +78,22 @@ public class VolumeObject implements VolumeInfo { return volumeVO.getState(); } + @Override public PrimaryDataStore getDataStore() { return dataStore; } + @Override public long getSize() { return volumeVO.getSize(); } + @Override public VolumeDiskType getDiskType() { return diskTypeHelper.getDiskType(volumeVO.getDiskType()); } + @Override public VolumeType getType() { return volumeTypeHelper.getType(volumeVO.getVolumeType()); } @@ -153,7 +159,7 @@ public class VolumeObject implements VolumeInfo { // TODO Auto-generated method stub return null; } - + @Override public String getName() { return this.volumeVO.getName(); 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 9236fbace4e..b6740edffd9 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 @@ -28,6 +28,7 @@ import java.util.Map; import java.util.Properties; import javax.ejb.Local; +import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; @@ -38,7 +39,6 @@ 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.Inject; import com.cloud.utils.component.PluggableService; /* @@ -60,8 +60,8 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIA private static List s_resourceDomainAdminCommands = null; private static List s_allCommands = null; - protected @Inject AccountManager _accountMgr; - @Inject protected List _services; + @Inject AccountManager _accountMgr; + @Inject List _services; protected StaticRoleBasedAPIAccessChecker() { super(); diff --git a/pom.xml b/pom.xml index 3abf731521d..c01e1255553 100644 --- a/pom.xml +++ b/pom.xml @@ -43,6 +43,7 @@ + true 1.6 UTF-8 @@ -88,7 +89,6 @@ 2.6 1.4 0.9.8 - true diff --git a/server/pom.xml b/server/pom.xml index b6d86e128b5..77f1fb816cd 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -90,6 +90,16 @@ cloud-engine-api ${project.version} + + org.apache.cloudstack + cloud-api + ${project.version} + + + org.apache.cloudstack + cloud-core + ${project.version} + install diff --git a/server/src/com/cloud/agent/manager/AgentManagerImpl.java b/server/src/com/cloud/agent/manager/AgentManagerImpl.java index ee5971f51c7..f7ca0349450 100755 --- a/server/src/com/cloud/agent/manager/AgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/AgentManagerImpl.java @@ -107,7 +107,6 @@ import com.cloud.user.AccountManager; import com.cloud.utils.ActionDelegate; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; @@ -151,7 +150,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { protected List> _creationMonitors = new ArrayList>(17); protected List _loadingAgents = new ArrayList(); protected int _monitorId = 0; - private Lock _agentStatusLock = new ReentrantLock(); + private final Lock _agentStatusLock = new ReentrantLock(); protected NioServer _connection; @Inject @@ -195,10 +194,10 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { @Inject protected VirtualMachineManager _vmMgr = null; - + @Inject StorageService _storageSvr = null; @Inject StorageManager _storageMgr = null; - + @Inject protected HypervisorGuruManager _hvGuruMgr; @@ -222,11 +221,11 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { protected ExecutorService _executor; protected ThreadPoolExecutor _connectExecutor; - + protected StateMachine2 _statusStateMachine = Status.getStateMachine(); - + @Inject ResourceManager _resourceMgr; - + @Override public boolean configure(final String name, final Map params) throws ConfigurationException { _name = name; @@ -263,7 +262,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { _nodeId = ManagementServerNode.getManagementServerId(); s_logger.info("Configuring AgentManagerImpl. management server node id(msid): " + _nodeId); - + long lastPing = (System.currentTimeMillis() >> 10) - _pingTimeout; _hostDao.markHostsAsDisconnected(_nodeId, lastPing); @@ -276,7 +275,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { new LinkedBlockingQueue(), new NamedThreadFactory("AgentConnectTaskPool")); //allow core threads to time out even when there are no items in the queue _connectExecutor.allowCoreThreadTimeOut(true); - + _connection = new NioServer("AgentManager", _port, workers + 10, this); s_logger.info("Listening on " + _port + " with " + workers + " workers"); @@ -395,7 +394,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } else if ( ssHost.getType() == Host.Type.SecondaryStorage) { sendToSSVM(ssHost.getDataCenterId(), cmd, listener); } else { - String err = "do not support Secondary Storage type " + ssHost.getType(); + String err = "do not support Secondary Storage type " + ssHost.getType(); s_logger.warn(err); throw new CloudRuntimeException(err); } @@ -435,7 +434,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } Answer answer = null; try { - + long targetHostId = _hvGuruMgr.getGuruProcessedCommandTargetHost(host.getId(), cmd); answer = easySend(targetHostId, cmd); } catch (Exception e) { @@ -552,7 +551,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { assert cmds.length > 0 : "Why are you sending zero length commands?"; if (cmds.length == 0) { - throw new AgentUnavailableException("Empty command set for agent " + agent.getId(), agent.getId()); + throw new AgentUnavailableException("Empty command set for agent " + agent.getId(), agent.getId()); } Request req = new Request(hostId, _nodeId, cmds, commands.stopOnError(), true); req.setSequence(agent.getNextSequence()); @@ -585,7 +584,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { if (removed != null) { removed.disconnect(nextState); } - + for (Pair monitor : _hostMonitors) { if (s_logger.isDebugEnabled()) { s_logger.debug("Sending Disconnect to listener: " + monitor.second().getClass().getName()); @@ -593,7 +592,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { monitor.second().processDisconnect(hostId, nextState); } } - + protected AgentAttache notifyMonitorsOfConnection(AgentAttache attache, final StartupCommand[] cmd, boolean forRebalance) throws ConnectionException { long hostId = attache.getId(); HostVO host = _hostDao.findById(hostId); @@ -678,7 +677,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { loadDirectlyConnectedHost(host, false); } } - + private ServerResource loadResourcesWithoutHypervisor(HostVO host){ String resourceName = host.getResource(); ServerResource resource = null; @@ -704,10 +703,10 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { if(resource != null){ _hostDao.loadDetails(host); - + HashMap params = new HashMap(host.getDetails().size() + 5); params.putAll(host.getDetails()); - + params.put("guid", host.getGuid()); params.put("zone", Long.toString(host.getDataCenterId())); if (host.getPodId() != null) { @@ -726,19 +725,19 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { params.put("pool", guid); } } - + params.put("ipaddress", host.getPrivateIpAddress()); params.put("secondary.storage.vm", "false"); params.put("max.template.iso.size", _configDao.getValue(Config.MaxTemplateAndIsoSize.toString())); params.put("migratewait", _configDao.getValue(Config.MigrateWait.toString())); - + try { resource.configure(host.getName(), params); } catch (ConfigurationException e) { s_logger.warn("Unable to configure resource due to " + e.getMessage()); return null; } - + if (!resource.start()) { s_logger.warn("Unable to start the resource"); return null; @@ -746,13 +745,13 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } return resource; } - + @SuppressWarnings("rawtypes") protected boolean loadDirectlyConnectedHost(HostVO host, boolean forRebalance) { - boolean initialized = false; + boolean initialized = false; ServerResource resource = null; - try { + try { //load the respective discoverer Discoverer discoverer = _resourceMgr.getMatchingDiscover(host.getHypervisorType()); if(discoverer == null){ @@ -761,20 +760,20 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { }else{ resource = discoverer.reloadResource(host); } - + if(resource == null){ s_logger.warn("Unable to load the resource: "+ host.getId()); return false; } - - initialized = true; - } finally { - if(!initialized) { + + initialized = true; + } finally { + if(!initialized) { if (host != null) { agentStatusTransitTo(host, Event.AgentDisconnected, _nodeId); } - } - } + } + } if (forRebalance) { Host h = _resourceMgr.createHostAndAgent(host.getId(), resource, host.getDetails(), false, null, true); @@ -790,10 +789,10 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { if (resource instanceof DummySecondaryStorageResource || resource instanceof KvmDummyResourceBase) { return new DummyAttache(this, host.getId(), false); } - + s_logger.debug("create DirectAgentAttache for " + host.getId()); DirectAgentAttache attache = new DirectAgentAttache(this, host.getId(), resource, host.isInMaintenanceStates(), this); - + AgentAttache old = null; synchronized (_agents) { old = _agents.put(host.getId(), attache); @@ -804,7 +803,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { return attache; } - + @Override public boolean stop() { if (_monitor != null) { @@ -823,13 +822,13 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { s_logger.debug("Cant not find host " + agent.getId()); } } else { - if (!agent.forForward()) { - agentStatusTransitTo(host, Event.ManagementServerDown, _nodeId); - } + if (!agent.forForward()) { + agentStatusTransitTo(host, Event.ManagementServerDown, _nodeId); + } } } } - + _connectExecutor.shutdownNow(); return true; } @@ -838,7 +837,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { public String getName() { return _name; } - + protected boolean handleDisconnectWithoutInvestigation(AgentAttache attache, Status.Event event, boolean transitState) { long hostId = attache.getId(); @@ -863,7 +862,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { s_logger.debug(err); throw new CloudRuntimeException(err); } - + if (s_logger.isDebugEnabled()) { s_logger.debug("The next status of agent " + hostId + "is " + nextStatus + ", current status is " + currentStatus); } @@ -876,15 +875,15 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { //remove the attache removeAgent(attache, nextStatus); - + //update the DB if (host != null && transitState) { - disconnectAgent(host, event, _nodeId); + disconnectAgent(host, event, _nodeId); } return true; } - + protected boolean handleDisconnectWithInvestigation(AgentAttache attache, Status.Event event) { long hostId = attache.getId(); HostVO host = _hostDao.findById(hostId); @@ -898,7 +897,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { * God knew what race condition the code dealt with! */ } - + if (nextStatus == Status.Alert) { /* OK, we are going to the bad status, let's see what happened */ s_logger.info("Investigating why host " + hostId + " has disconnected with event " + event); @@ -947,7 +946,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { s_logger.debug("The next status of Agent " + host.getId() + " is not Alert, no need to investigate what happened"); } } - + handleDisconnectWithoutInvestigation(attache, event, true); host = _hostDao.findById(host.getId()); if (host.getStatus() == Status.Alert || host.getStatus() == Status.Down) { @@ -970,11 +969,11 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { @Override public void run() { - try { + try { if (_investigate == true) { handleDisconnectWithInvestigation(_attache, _event); } else { - handleDisconnectWithoutInvestigation(_attache, _event, true); + handleDisconnectWithoutInvestigation(_attache, _event, true); } } catch (final Exception e) { s_logger.error("Exception caught while handling disconnect: ", e); @@ -1059,14 +1058,14 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { @Override public boolean executeUserRequest(long hostId, Event event) throws AgentUnavailableException { - if (event == Event.AgentDisconnected) { + if (event == Event.AgentDisconnected) { if (s_logger.isDebugEnabled()) { s_logger.debug("Received agent disconnect event for host " + hostId); } AgentAttache attache = null; attache = findAttache(hostId); if (attache != null) { - handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected, true); + handleDisconnectWithoutInvestigation(attache, Event.AgentDisconnected, true); } return true; } else if (event == Event.ShutdownRequested) { @@ -1079,7 +1078,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { s_logger.debug("create ConnectedAgentAttache for " + host.getId()); AgentAttache attache = new ConnectedAgentAttache(this, host.getId(), link, host.isInMaintenanceStates()); link.attach(attache); - + AgentAttache old = null; synchronized (_agents) { old = _agents.put(host.getId(), attache); @@ -1090,36 +1089,36 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { return attache; } - + private AgentAttache handleConnectedAgent(final Link link, final StartupCommand[] startup, Request request) { - AgentAttache attache = null; - ReadyCommand ready = null; - try { - HostVO host = _resourceMgr.createHostVOForConnectedAgent(startup); - if (host != null) { - ready = new ReadyCommand(host.getDataCenterId(), host.getId()); - attache = createAttacheForConnect(host, link); - attache = notifyMonitorsOfConnection(attache, startup, false); - } + AgentAttache attache = null; + ReadyCommand ready = null; + try { + HostVO host = _resourceMgr.createHostVOForConnectedAgent(startup); + if (host != null) { + ready = new ReadyCommand(host.getDataCenterId(), host.getId()); + attache = createAttacheForConnect(host, link); + attache = notifyMonitorsOfConnection(attache, startup, false); + } } catch (Exception e) { - s_logger.debug("Failed to handle host connection: " + e.toString()); - ready = new ReadyCommand(null); - ready.setDetails(e.toString()); + s_logger.debug("Failed to handle host connection: " + e.toString()); + ready = new ReadyCommand(null); + ready.setDetails(e.toString()); } finally { if (ready == null) { ready = new ReadyCommand(null); - } + } } - + try { - if (attache == null) { - final Request readyRequest = new Request(-1, -1, ready, false); - link.send(readyRequest.getBytes()); - } else { - easySend(attache.getId(), ready); - } + if (attache == null) { + final Request readyRequest = new Request(-1, -1, ready, false); + link.send(readyRequest.getBytes()); + } else { + easySend(attache.getId(), ready); + } } catch (Exception e) { - s_logger.debug("Failed to send ready command:" + e.toString()); + s_logger.debug("Failed to send ready command:" + e.toString()); } return attache; } @@ -1143,7 +1142,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { if (s_logger.isDebugEnabled()) { s_logger.debug("Simulating start for resource " + resource.getName() + " id " + id); } - + _resourceMgr.createHostAndAgent(id, resource, details, false, null, false); } catch (Exception e) { s_logger.warn("Unable to simulate start on resource " + id + " name " + resource.getName(), e); @@ -1174,32 +1173,32 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { for (int i = 0; i < _cmds.length; i++) { startups[i] = (StartupCommand) _cmds[i]; } - + AgentAttache attache = handleConnectedAgent(_link, startups, _request); if (attache == null) { s_logger.warn("Unable to create attache for agent: " + _request); } } } - + protected void connectAgent(Link link, final Command[] cmds, final Request request) { - //send startupanswer to agent in the very beginning, so agent can move on without waiting for the answer for an undetermined time, if we put this logic into another thread pool. - StartupAnswer[] answers = new StartupAnswer[cmds.length]; - Command cmd; - for (int i = 0; i < cmds.length; i++) { - cmd = cmds[i]; - if ((cmd instanceof StartupRoutingCommand) || (cmd instanceof StartupProxyCommand) || (cmd instanceof StartupSecondaryStorageCommand) || (cmd instanceof StartupStorageCommand)) { - answers[i] = new StartupAnswer((StartupCommand)cmds[i], 0, getPingInterval()); - break; - } - } - Response response = null; - response = new Response(request, answers[0], _nodeId, -1); - try { - link.send(response.toBytes()); - } catch (ClosedChannelException e) { - s_logger.debug("Failed to send startupanswer: " + e.toString()); - } + //send startupanswer to agent in the very beginning, so agent can move on without waiting for the answer for an undetermined time, if we put this logic into another thread pool. + StartupAnswer[] answers = new StartupAnswer[cmds.length]; + Command cmd; + for (int i = 0; i < cmds.length; i++) { + cmd = cmds[i]; + if ((cmd instanceof StartupRoutingCommand) || (cmd instanceof StartupProxyCommand) || (cmd instanceof StartupSecondaryStorageCommand) || (cmd instanceof StartupStorageCommand)) { + answers[i] = new StartupAnswer((StartupCommand)cmds[i], 0, getPingInterval()); + break; + } + } + Response response = null; + response = new Response(request, answers[0], _nodeId, -1); + try { + link.send(response.toBytes()); + } catch (ClosedChannelException e) { + s_logger.debug("Failed to send startupanswer: " + e.toString()); + } _connectExecutor.execute(new HandleAgentConnectTask(link, cmds, request)); } @@ -1215,14 +1214,14 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { boolean logD = true; if (attache == null) { - if (!(cmd instanceof StartupCommand)) { - s_logger.warn("Throwing away a request because it came through as the first command on a connect: " + request); - } else { - //submit the task for execution - request.logD("Scheduling the first command "); - connectAgent(link, cmds, request); - } - return; + if (!(cmd instanceof StartupCommand)) { + s_logger.warn("Throwing away a request because it came through as the first command on a connect: " + request); + } else { + //submit the task for execution + request.logD("Scheduling the first command "); + connectAgent(link, cmds, request); + } + return; } final long hostId = attache.getId(); @@ -1286,20 +1285,20 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { if (cmd instanceof PingRoutingCommand) { boolean gatewayAccessible = ((PingRoutingCommand) cmd).isGatewayAccessible(); HostVO host = _hostDao.findById(Long.valueOf(cmdHostId)); - - if (host != null) { - if (!gatewayAccessible) { - // alert that host lost connection to - // gateway (cannot ping the default route) - DataCenterVO dcVO = _dcDao.findById(host.getDataCenterId()); - HostPodVO podVO = _podDao.findById(host.getPodId()); - String hostDesc = "name: " + host.getName() + " (id:" + host.getId() + "), availability zone: " + dcVO.getName() + ", pod: " + podVO.getName(); - _alertMgr.sendAlert(AlertManager.ALERT_TYPE_ROUTING, host.getDataCenterId(), host.getPodId(), "Host lost connection to gateway, " + hostDesc, "Host [" + hostDesc - + "] lost connection to gateway (default route) and is possibly having network connection issues."); - } else { - _alertMgr.clearAlert(AlertManager.ALERT_TYPE_ROUTING, host.getDataCenterId(), host.getPodId()); - } + if (host != null) { + if (!gatewayAccessible) { + // alert that host lost connection to + // gateway (cannot ping the default route) + DataCenterVO dcVO = _dcDao.findById(host.getDataCenterId()); + HostPodVO podVO = _podDao.findById(host.getPodId()); + String hostDesc = "name: " + host.getName() + " (id:" + host.getId() + "), availability zone: " + dcVO.getName() + ", pod: " + podVO.getName(); + + _alertMgr.sendAlert(AlertManager.ALERT_TYPE_ROUTING, host.getDataCenterId(), host.getPodId(), "Host lost connection to gateway, " + hostDesc, "Host [" + hostDesc + + "] lost connection to gateway (default route) and is possibly having network connection issues."); + } else { + _alertMgr.clearAlert(AlertManager.ALERT_TYPE_ROUTING, host.getDataCenterId(), host.getPodId()); + } } else { s_logger.debug("Not processing " + PingRoutingCommand.class.getSimpleName() + " for agent id=" + cmdHostId + "; can't find the host in the DB"); @@ -1391,7 +1390,7 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { protected AgentManagerImpl() { } - @Override + @Override public boolean tapLoadingAgents(Long hostId, TapAgentsAction action) { synchronized (_loadingAgents) { if (action == TapAgentsAction.Add) { @@ -1406,58 +1405,58 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } return true; } - + @Override public boolean agentStatusTransitTo(HostVO host, Status.Event e, long msId) { - try { - _agentStatusLock.lock(); - if (status_logger.isDebugEnabled()) { - ResourceState state = host.getResourceState(); - StringBuilder msg = new StringBuilder("Transition:"); - msg.append("[Resource state = ").append(state); - msg.append(", Agent event = ").append(e.toString()); - msg.append(", Host id = ").append(host.getId()).append(", name = " + host.getName()).append("]"); - status_logger.debug(msg); - } + try { + _agentStatusLock.lock(); + if (status_logger.isDebugEnabled()) { + ResourceState state = host.getResourceState(); + StringBuilder msg = new StringBuilder("Transition:"); + msg.append("[Resource state = ").append(state); + msg.append(", Agent event = ").append(e.toString()); + msg.append(", Host id = ").append(host.getId()).append(", name = " + host.getName()).append("]"); + status_logger.debug(msg); + } - host.setManagementServerId(msId); - try { - return _statusStateMachine.transitTo(host, e, host.getId(), _hostDao); - } catch (NoTransitionException e1) { - status_logger.debug("Cannot transit agent status with event " + e + " for host " + host.getId() + ", name=" + host.getName() - + ", mangement server id is " + msId); - throw new CloudRuntimeException("Cannot transit agent status with event " + e + " for host " + host.getId() + ", mangement server id is " - + msId + "," + e1.getMessage()); - } - } finally { - _agentStatusLock.unlock(); - } + host.setManagementServerId(msId); + try { + return _statusStateMachine.transitTo(host, e, host.getId(), _hostDao); + } catch (NoTransitionException e1) { + status_logger.debug("Cannot transit agent status with event " + e + " for host " + host.getId() + ", name=" + host.getName() + + ", mangement server id is " + msId); + throw new CloudRuntimeException("Cannot transit agent status with event " + e + " for host " + host.getId() + ", mangement server id is " + + msId + "," + e1.getMessage()); + } + } finally { + _agentStatusLock.unlock(); + } } - + public boolean disconnectAgent(HostVO host, Status.Event e, long msId) { host.setDisconnectedOn(new Date()); if (e.equals(Status.Event.Remove)) { host.setGuid(null); host.setClusterId(null); } - + return agentStatusTransitTo(host, e, msId); } - + protected void disconnectWithoutInvestigation(AgentAttache attache, final Status.Event event) { _executor.submit(new DisconnectTask(attache, event, false)); } - + protected void disconnectWithInvestigation(AgentAttache attache, final Status.Event event) { _executor.submit(new DisconnectTask(attache, event, true)); } - + private void disconnectInternal(final long hostId, final Status.Event event, boolean invstigate) { AgentAttache attache = findAttache(hostId); if (attache != null) { if (!invstigate) { - disconnectWithoutInvestigation(attache, event); + disconnectWithoutInvestigation(attache, event); } else { disconnectWithInvestigation(attache, event); } @@ -1470,35 +1469,35 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { HostVO host = _hostDao.findById(hostId); if (host != null && host.getRemoved() == null) { - disconnectAgent(host, event, _nodeId); + disconnectAgent(host, event, _nodeId); } } } - + public void disconnectWithInvestigation(final long hostId, final Status.Event event) { disconnectInternal(hostId, event, true); } - + @Override public void disconnectWithoutInvestigation(final long hostId, final Status.Event event) { disconnectInternal(hostId, event, false); } - @Override + @Override public AgentAttache handleDirectConnectAgent(HostVO host, StartupCommand[] cmds, ServerResource resource, boolean forRebalance) throws ConnectionException { - AgentAttache attache; - - attache = createAttacheForDirectConnect(host, resource); + AgentAttache attache; + + attache = createAttacheForDirectConnect(host, resource); StartupAnswer[] answers = new StartupAnswer[cmds.length]; for (int i = 0; i < answers.length; i++) { answers[i] = new StartupAnswer(cmds[i], attache.getId(), _pingInterval); } attache.process(answers); - attache = notifyMonitorsOfConnection(attache, cmds, forRebalance); - - return attache; + attache = notifyMonitorsOfConnection(attache, cmds, forRebalance); + + return attache; } - + @Override public void pullAgentToMaintenance(long hostId) { AgentAttache attache = findAttache(hostId); @@ -1508,15 +1507,15 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { attache.cancelAllCommands(Status.Disconnected, false); } } - + @Override public void pullAgentOutMaintenance(long hostId) { AgentAttache attache = findAttache(hostId); if (attache != null) { - attache.setMaintenanceMode(false); + attache.setMaintenanceMode(false); } } - - - + + + } diff --git a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java index 6753b280961..dbd7cd3ed47 100755 --- a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java @@ -80,8 +80,6 @@ import com.cloud.resource.ServerResource; import com.cloud.storage.resource.DummySecondaryStorageResource; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.component.Adapters; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.SearchCriteria2; @@ -116,10 +114,10 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust protected ManagementServerHostDao _mshostDao; @Inject protected HostTransferMapDao _hostTransferDao; - + // @com.cloud.utils.component.Inject(adapter = AgentLoadBalancerPlanner.class) @Inject protected List _lbPlanners; - + @Inject protected AgentManager _agentMgr; @Inject ConfigurationDao _configDao; @@ -133,7 +131,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust _peers = new HashMap(7); _sslEngines = new HashMap(7); _nodeId = _clusterMgr.getManagementNodeId(); - + s_logger.info("Configuring ClusterAgentManagerImpl. management server node id(msid): " + _nodeId); Map params = _configDao.getConfiguration(xmlParams); @@ -143,7 +141,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust ClusteredAgentAttache.initialize(this); _clusterMgr.registerListener(this); - + return super.configure(name, xmlParams); } @@ -177,7 +175,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust List hosts = _hostDao.findAndUpdateDirectAgentToLoad(cutSeconds, _loadSize, _nodeId); List appliances = _hostDao.findAndUpdateApplianceToLoad(cutSeconds, _nodeId); hosts.addAll(appliances); - + if (hosts != null && hosts.size() > 0) { s_logger.debug("Found " + hosts.size() + " unmanaged direct hosts, processing connect for them..."); for (HostVO host : hosts) { @@ -278,12 +276,12 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust protected boolean handleDisconnectWithoutInvestigation(AgentAttache attache, Status.Event event, boolean transitState) { return handleDisconnect(attache, event, false, true); } - + @Override protected boolean handleDisconnectWithInvestigation(AgentAttache attache, Status.Event event) { return handleDisconnect(attache, event, true, true); } - + protected boolean handleDisconnect(AgentAttache agent, Status.Event event, boolean investigate, boolean broadcast) { boolean res; if (!investigate) { @@ -292,14 +290,14 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust res = super.handleDisconnectWithInvestigation(agent, event); } - if (res) { - if (broadcast) { - notifyNodesInCluster(agent); - } - return true; - } else { - return false; - } + if (res) { + if (broadcast) { + notifyNodesInCluster(agent); + } + return true; + } else { + return false; + } } @Override @@ -343,15 +341,15 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust public boolean reconnect(final long hostId) { Boolean result; try { - result = _clusterMgr.propagateAgentEvent(hostId, Event.ShutdownRequested); - if (result != null) { - return result; - } + result = _clusterMgr.propagateAgentEvent(hostId, Event.ShutdownRequested); + if (result != null) { + return result; + } } catch (AgentUnavailableException e) { - s_logger.debug("cannot propagate agent reconnect because agent is not available", e); - return false; + s_logger.debug("cannot propagate agent reconnect because agent is not available", e); + return false; } - + return super.reconnect(hostId); } @@ -413,7 +411,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust public String findPeer(long hostId) { return _clusterMgr.getPeerName(hostId); } - + public SSLEngine getSSLEngine(String peerName) { return _sslEngines.get(peerName); } @@ -520,7 +518,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } } if (agent == null) { - AgentUnavailableException ex = new AgentUnavailableException("Host with specified id is not in the right state: " + host.getStatus(), hostId); + AgentUnavailableException ex = new AgentUnavailableException("Host with specified id is not in the right state: " + host.getStatus(), hostId); ex.addProxyObject(ApiDBUtils.findHostById(hostId).getUuid()); throw ex; } @@ -540,11 +538,11 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } } _timer.cancel(); - + //cancel all transfer tasks s_transferExecutor.shutdownNow(); cleanupTransferMap(_nodeId); - + return super.stop(); } @@ -698,19 +696,19 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust @Override public boolean executeRebalanceRequest(long agentId, long currentOwnerId, long futureOwnerId, Event event) throws AgentUnavailableException, OperationTimedoutException { - boolean result = false; + boolean result = false; if (event == Event.RequestAgentRebalance) { return setToWaitForRebalance(agentId, currentOwnerId, futureOwnerId); } else if (event == Event.StartAgentRebalance) { try { - result = rebalanceHost(agentId, currentOwnerId, futureOwnerId); + result = rebalanceHost(agentId, currentOwnerId, futureOwnerId); } catch (Exception e) { s_logger.warn("Unable to rebalance host id=" + agentId, e); } } return result; } - + @Override public void scheduleRebalanceAgents() { _timer.schedule(new AgentLoadBalancerTask(), 30000); @@ -735,20 +733,20 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust @Override public synchronized void run() { - try { - if (!cancelled) { - startRebalanceAgents(); - if (s_logger.isInfoEnabled()) { - s_logger.info("The agent load balancer task is now being cancelled"); - } - cancelled = true; - } - } catch(Throwable e) { - s_logger.error("Unexpected exception " + e.toString(), e); - } + try { + if (!cancelled) { + startRebalanceAgents(); + if (s_logger.isInfoEnabled()) { + s_logger.info("The agent load balancer task is now being cancelled"); + } + cancelled = true; + } + } catch(Throwable e) { + s_logger.error("Unexpected exception " + e.toString(), e); + } } } - + public void startRebalanceAgents() { s_logger.debug("Management server " + _nodeId + " is asking other peers to rebalance their agents"); List allMS = _mshostDao.listBy(ManagementServerHost.State.Up); @@ -767,7 +765,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } return; } - + if (avLoad == 0L) { if (s_logger.isDebugEnabled()) { s_logger.debug("As calculated average load is less than 1, rounding it to 1"); @@ -777,7 +775,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust for (ManagementServerHostVO node : allMS) { if (node.getMsid() != _nodeId) { - + List hostsToRebalance = new ArrayList(); for (AgentLoadBalancerPlanner lbPlanner : _lbPlanners) { hostsToRebalance = lbPlanner.getHostsToRebalance(node.getMsid(), avLoad); @@ -788,14 +786,14 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } } - + if (hostsToRebalance != null && !hostsToRebalance.isEmpty()) { s_logger.debug("Found " + hostsToRebalance.size() + " hosts to rebalance from management server " + node.getMsid()); for (HostVO host : hostsToRebalance) { long hostId = host.getId(); s_logger.debug("Asking management server " + node.getMsid() + " to give away host id=" + hostId); boolean result = true; - + if (_hostTransferDao.findById(hostId) != null) { s_logger.warn("Somebody else is already rebalancing host id: " + hostId); continue; @@ -867,7 +865,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust for (Iterator iterator = _agentToTransferIds.iterator(); iterator.hasNext();) { Long hostId = iterator.next(); AgentAttache attache = findAttache(hostId); - + // if the thread: // 1) timed out waiting for the host to reconnect // 2) recipient management server is not active any more @@ -883,14 +881,14 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust _hostTransferDao.completeAgentTransfer(hostId); continue; } - + if (transferMap.getInitialOwner() != _nodeId || attache == null || attache.forForward()) { s_logger.debug("Management server " + _nodeId + " doesn't own host id=" + hostId + " any more, skipping rebalance for the host"); iterator.remove(); _hostTransferDao.completeAgentTransfer(hostId); continue; } - + ManagementServerHostVO ms = _mshostDao.findByMsid(transferMap.getFutureOwner()); if (ms != null && ms.getState() != ManagementServerHost.State.Up) { s_logger.debug("Can't transfer host " + hostId + " as it's future owner is not in UP state: " + ms + ", skipping rebalance for the host"); @@ -898,7 +896,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust _hostTransferDao.completeAgentTransfer(hostId); continue; } - + if (attache.getQueueSize() == 0 && attache.getNonRecurringListenersSize() == 0) { iterator.remove(); try { @@ -907,7 +905,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust s_logger.warn("Failed to submit rebalance task for host id=" + hostId + "; postponing the execution"); continue; } - + } else { s_logger.debug("Agent " + hostId + " can't be transfered yet as its request queue size is " + attache.getQueueSize() + " and listener queue size is " + attache.getNonRecurringListenersSize()); } @@ -925,16 +923,16 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } }; } - - + + private boolean setToWaitForRebalance(final long hostId, long currentOwnerId, long futureOwnerId) { s_logger.debug("Adding agent " + hostId + " to the list of agents to transfer"); synchronized (_agentToTransferIds) { return _agentToTransferIds.add(hostId); } } - - + + protected boolean rebalanceHost(final long hostId, long currentOwnerId, long futureOwnerId) throws AgentUnavailableException{ boolean result = true; @@ -954,7 +952,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust s_logger.warn("Host " + hostId + " failed to connect to the management server " + futureOwnerId + " as a part of rebalance process", ex); result = false; } - + if (result) { s_logger.debug("Successfully transfered host id=" + hostId + " to management server " + futureOwnerId); finishRebalance(hostId, futureOwnerId, Event.RebalanceCompleted); @@ -962,7 +960,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust s_logger.warn("Failed to transfer host id=" + hostId + " to management server " + futureOwnerId); finishRebalance(hostId, futureOwnerId, Event.RebalanceFailed); } - + } else if (futureOwnerId == _nodeId) { HostVO host = _hostDao.findById(hostId); try { @@ -977,9 +975,9 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust if (result) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Loading directly connected host " + host.getId() + "(" + host.getName() + ") to the management server " + _nodeId + " as a part of rebalance process"); - } - result = loadDirectlyConnectedHost(host, true); + s_logger.debug("Loading directly connected host " + host.getId() + "(" + host.getName() + ") to the management server " + _nodeId + " as a part of rebalance process"); + } + result = loadDirectlyConnectedHost(host, true); } else { s_logger.warn("Failed to disconnect " + host.getId() + "(" + host.getName() + " as a part of rebalance process without notification"); @@ -989,7 +987,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust s_logger.warn("Failed to load directly connected host " + host.getId() + "(" + host.getName() + ") to the management server " + _nodeId + " as a part of rebalance process due to:", ex); result = false; } - + if (result) { s_logger.debug("Successfully loaded directly connected host " + host.getId() + "(" + host.getName() + ") to the management server " + _nodeId + " as a part of rebalance process"); } else { @@ -999,7 +997,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust return result; } - + protected void finishRebalance(final long hostId, long futureOwnerId, Event event){ @@ -1007,21 +1005,21 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust if (s_logger.isDebugEnabled()) { s_logger.debug("Finishing rebalancing for the agent " + hostId + " with event " + event); } - + AgentAttache attache = findAttache(hostId); if (attache == null || !(attache instanceof ClusteredAgentAttache)) { s_logger.debug("Unable to find forward attache for the host id=" + hostId + ", assuming that the agent disconnected already"); _hostTransferDao.completeAgentTransfer(hostId); return; } - + ClusteredAgentAttache forwardAttache = (ClusteredAgentAttache)attache; - + if (success) { //1) Set transfer mode to false - so the agent can start processing requests normally forwardAttache.setTransferMode(false); - + //2) Get all transfer requests and route them to peer Request requestToTransfer = forwardAttache.getRequestToTransfer(); while (requestToTransfer != null) { @@ -1030,20 +1028,20 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust if (!routeResult) { logD(requestToTransfer.getBytes(), "Failed to route request to peer"); } - + requestToTransfer = forwardAttache.getRequestToTransfer(); } - + s_logger.debug("Management server " + _nodeId + " completed agent " + hostId + " rebalance to " + futureOwnerId); - + } else { failRebalance(hostId); } - + s_logger.debug("Management server " + _nodeId + " completed agent " + hostId + " rebalance"); _hostTransferDao.completeAgentTransfer(hostId); } - + protected void failRebalance(final long hostId){ try { s_logger.debug("Management server " + _nodeId + " failed to rebalance agent " + hostId); @@ -1053,19 +1051,19 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust s_logger.warn("Failed to reconnect host id=" + hostId + " as a part of failed rebalance task cleanup"); } } - + protected boolean startRebalance(final long hostId) { HostVO host = _hostDao.findById(hostId); - + if (host == null || host.getRemoved() != null) { s_logger.warn("Unable to find host record, fail start rebalancing process"); return false; } - + synchronized (_agents) { ClusteredDirectAgentAttache attache = (ClusteredDirectAgentAttache)_agents.get(hostId); if (attache != null && attache.getQueueSize() == 0 && attache.getNonRecurringListenersSize() == 0) { - handleDisconnectWithoutInvestigation(attache, Event.StartAgentRebalance, true); + handleDisconnectWithoutInvestigation(attache, Event.StartAgentRebalance, true); ClusteredAgentAttache forwardAttache = (ClusteredAgentAttache)createAttache(hostId); if (forwardAttache == null) { s_logger.warn("Unable to create a forward attache for the host " + hostId + " as a part of rebalance process"); @@ -1086,27 +1084,27 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust _hostTransferDao.startAgentTransfer(hostId); return true; } - + protected void cleanupTransferMap(long msId) { List hostsJoingingCluster = _hostTransferDao.listHostsJoiningCluster(msId); - + for (HostTransferMapVO hostJoingingCluster : hostsJoingingCluster) { _hostTransferDao.remove(hostJoingingCluster.getId()); } - + List hostsLeavingCluster = _hostTransferDao.listHostsLeavingCluster(msId); for (HostTransferMapVO hostLeavingCluster : hostsLeavingCluster) { _hostTransferDao.remove(hostLeavingCluster.getId()); } } - - + + protected class RebalanceTask implements Runnable { Long hostId = null; Long currentOwnerId = null; Long futureOwnerId = null; - - + + public RebalanceTask(long hostId, long currentOwnerId, long futureOwnerId) { this.hostId = hostId; this.currentOwnerId = currentOwnerId; @@ -1127,5 +1125,5 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } } } - + } diff --git a/server/src/com/cloud/agent/manager/allocator/impl/TestingAllocator.java b/server/src/com/cloud/agent/manager/allocator/impl/TestingAllocator.java index 9951896ffd6..c8bbe02aece 100755 --- a/server/src/com/cloud/agent/manager/allocator/impl/TestingAllocator.java +++ b/server/src/com/cloud/agent/manager/allocator/impl/TestingAllocator.java @@ -26,14 +26,12 @@ import javax.inject.Inject; import org.springframework.stereotype.Component; import com.cloud.agent.manager.allocator.HostAllocator; -import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.host.dao.HostDao; import com.cloud.offering.ServiceOffering; -import com.cloud.utils.component.ComponentLocator; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; @@ -51,19 +49,19 @@ public class TestingAllocator implements HostAllocator { ExcludeList avoid, int returnUpTo) { return allocateTo(vmProfile, plan, type, avoid, returnUpTo, true); } - + @Override public List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, - ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity) { - List availableHosts = new ArrayList(); - Host host = null; + ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity) { + List availableHosts = new ArrayList(); + Host host = null; if (type == Host.Type.Routing && _routingHost != null) { - host = _hostDao.findById(_routingHost); + host = _hostDao.findById(_routingHost); } else if (type == Host.Type.Storage && _storageHost != null) { - host = _hostDao.findById(_storageHost); + host = _hostDao.findById(_storageHost); } if(host != null){ - availableHosts.add(host); + availableHosts.add(host); } return availableHosts; } @@ -82,9 +80,9 @@ public class TestingAllocator implements HostAllocator { value = (String)params.get(Host.Type.Storage.toString()); _storageHost = (value != null) ? Long.parseLong(value) : null; - + _name = name; - + return true; } diff --git a/server/src/com/cloud/alert/AlertManagerImpl.java b/server/src/com/cloud/alert/AlertManagerImpl.java index 2ad21eda9c1..bcd364e8cd3 100755 --- a/server/src/com/cloud/alert/AlertManagerImpl.java +++ b/server/src/com/cloud/alert/AlertManagerImpl.java @@ -72,7 +72,6 @@ import com.cloud.storage.StoragePoolVO; import com.cloud.storage.dao.StoragePoolDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.SearchCriteria; import com.sun.mail.smtp.SMTPMessage; @@ -116,9 +115,9 @@ public class AlertManagerImpl implements AlertManager { private double _publicIPCapacityThreshold = 0.75; private double _privateIPCapacityThreshold = 0.75; private double _secondaryStorageCapacityThreshold = 0.75; - private double _vlanCapacityThreshold = 0.75; - private double _directNetworkPublicIpCapacityThreshold = 0.75; - private double _localStorageCapacityThreshold = 0.75; + private double _vlanCapacityThreshold = 0.75; + private double _directNetworkPublicIpCapacityThreshold = 0.75; + private double _localStorageCapacityThreshold = 0.75; Map _capacityTypeThresholdMap = new HashMap(); @Override @@ -149,7 +148,7 @@ public class AlertManagerImpl implements AlertManager { _emailAlert = new EmailAlert(emailAddresses, smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, smtpDebug); - + String storageCapacityThreshold = _configDao.getValue(Config.StorageCapacityThreshold.key()); String cpuCapacityThreshold = _configDao.getValue(Config.CPUCapacityThreshold.key()); String memoryCapacityThreshold = _configDao.getValue(Config.MemoryCapacityThreshold.key()); @@ -160,7 +159,7 @@ public class AlertManagerImpl implements AlertManager { String vlanCapacityThreshold = _configDao.getValue(Config.VlanCapacityThreshold.key()); String directNetworkPublicIpCapacityThreshold = _configDao.getValue(Config.DirectNetworkPublicIpCapacityThreshold.key()); String localStorageCapacityThreshold = _configDao.getValue(Config.LocalStorageCapacityThreshold.key()); - + if (storageCapacityThreshold != null) { _storageCapacityThreshold = Double.parseDouble(storageCapacityThreshold); } @@ -174,10 +173,10 @@ public class AlertManagerImpl implements AlertManager { _memoryCapacityThreshold = Double.parseDouble(memoryCapacityThreshold); } if (publicIPCapacityThreshold != null) { - _publicIPCapacityThreshold = Double.parseDouble(publicIPCapacityThreshold); + _publicIPCapacityThreshold = Double.parseDouble(publicIPCapacityThreshold); } if (privateIPCapacityThreshold != null) { - _privateIPCapacityThreshold = Double.parseDouble(privateIPCapacityThreshold); + _privateIPCapacityThreshold = Double.parseDouble(privateIPCapacityThreshold); } if (secondaryStorageCapacityThreshold != null) { _secondaryStorageCapacityThreshold = Double.parseDouble(secondaryStorageCapacityThreshold); @@ -191,7 +190,7 @@ public class AlertManagerImpl implements AlertManager { if (localStorageCapacityThreshold != null) { _localStorageCapacityThreshold = Double.parseDouble(localStorageCapacityThreshold); } - + _capacityTypeThresholdMap.put(Capacity.CAPACITY_TYPE_STORAGE, _storageCapacityThreshold); _capacityTypeThresholdMap.put(Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED, _storageAllocCapacityThreshold); _capacityTypeThresholdMap.put(Capacity.CAPACITY_TYPE_CPU, _cpuCapacityThreshold); @@ -203,19 +202,19 @@ public class AlertManagerImpl implements AlertManager { _capacityTypeThresholdMap.put(Capacity.CAPACITY_TYPE_DIRECT_ATTACHED_PUBLIC_IP, _directNetworkPublicIpCapacityThreshold); _capacityTypeThresholdMap.put(Capacity.CAPACITY_TYPE_LOCAL_STORAGE, _localStorageCapacityThreshold); - + String capacityCheckPeriodStr = configs.get("capacity.check.period"); if (capacityCheckPeriodStr != null) { _capacityCheckPeriod = Long.parseLong(capacityCheckPeriodStr); if(_capacityCheckPeriod <= 0) - _capacityCheckPeriod = Long.parseLong(Config.CapacityCheckPeriod.getDefaultValue()); + _capacityCheckPeriod = Long.parseLong(Config.CapacityCheckPeriod.getDefaultValue()); } - + String cpuOverProvisioningFactorStr = configs.get("cpu.overprovisioning.factor"); if (cpuOverProvisioningFactorStr != null) { _cpuOverProvisioningFactor = NumbersUtil.parseFloat(cpuOverProvisioningFactorStr,1); if(_cpuOverProvisioningFactor < 1){ - _cpuOverProvisioningFactor = 1; + _cpuOverProvisioningFactor = 1; } } @@ -273,122 +272,122 @@ public class AlertManagerImpl implements AlertManager { // is stopped we updated the amount allocated, and when VM sync reports a changed state, we update // the amount allocated. Hopefully it's limited to 3 entry points and will keep the amount allocated // per host accurate. - + try { - - if (s_logger.isDebugEnabled()) { + + if (s_logger.isDebugEnabled()) { s_logger.debug("recalculating system capacity"); s_logger.debug("Executing cpu/ram capacity update"); } - - // Calculate CPU and RAM capacities - // get all hosts...even if they are not in 'UP' state - List hosts = _resourceMgr.listAllNotInMaintenanceHostsInOneZone(Host.Type.Routing, null); - for (HostVO host : hosts) { - _capacityMgr.updateCapacityForHost(host); - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Done executing cpu/ram capacity update"); - s_logger.debug("Executing storage capacity update"); - } - // Calculate storage pool capacity - List storagePools = _storagePoolDao.listAll(); - for (StoragePoolVO pool : storagePools) { - long disk = _capacityMgr.getAllocatedPoolCapacity(pool, null); - if (pool.isShared()){ - _storageMgr.createCapacityEntry(pool, Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED, disk); - }else { - _storageMgr.createCapacityEntry(pool, Capacity.CAPACITY_TYPE_LOCAL_STORAGE, disk); - } - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Done executing storage capacity update"); - s_logger.debug("Executing capacity updates for public ip and Vlans"); - } - List datacenters = _dcDao.listAll(); - for (DataCenterVO datacenter : datacenters) { - long dcId = datacenter.getId(); - - //NOTE - //What happens if we have multiple vlans? Dashboard currently shows stats - //with no filter based on a vlan - //ideal way would be to remove out the vlan param, and filter only on dcId - //implementing the same - - // Calculate new Public IP capacity for Virtual Network - if (datacenter.getNetworkType() == NetworkType.Advanced){ - createOrUpdateIpCapacity(dcId, null, CapacityVO.CAPACITY_TYPE_VIRTUAL_NETWORK_PUBLIC_IP, datacenter.getAllocationState()); - } - - // Calculate new Public IP capacity for Direct Attached Network - createOrUpdateIpCapacity(dcId, null, CapacityVO.CAPACITY_TYPE_DIRECT_ATTACHED_PUBLIC_IP, datacenter.getAllocationState()); - - if (datacenter.getNetworkType() == NetworkType.Advanced){ - //Calculate VLAN's capacity - createOrUpdateVlanCapacity(dcId, datacenter.getAllocationState()); + // Calculate CPU and RAM capacities + // get all hosts...even if they are not in 'UP' state + List hosts = _resourceMgr.listAllNotInMaintenanceHostsInOneZone(Host.Type.Routing, null); + for (HostVO host : hosts) { + _capacityMgr.updateCapacityForHost(host); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Done executing cpu/ram capacity update"); + s_logger.debug("Executing storage capacity update"); + } + // Calculate storage pool capacity + List storagePools = _storagePoolDao.listAll(); + for (StoragePoolVO pool : storagePools) { + long disk = _capacityMgr.getAllocatedPoolCapacity(pool, null); + if (pool.isShared()){ + _storageMgr.createCapacityEntry(pool, Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED, disk); + }else { + _storageMgr.createCapacityEntry(pool, Capacity.CAPACITY_TYPE_LOCAL_STORAGE, disk); } - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Done capacity updates for public ip and Vlans"); - s_logger.debug("Executing capacity updates for private ip"); - } - - // Calculate new Private IP capacity - List pods = _podDao.listAll(); - for (HostPodVO pod : pods) { - long podId = pod.getId(); - long dcId = pod.getDataCenterId(); + } - createOrUpdateIpCapacity(dcId, podId, CapacityVO.CAPACITY_TYPE_PRIVATE_IP, _configMgr.findPodAllocationState(pod)); - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Done executing capacity updates for private ip"); - s_logger.debug("Done recalculating system capacity"); - } + if (s_logger.isDebugEnabled()) { + s_logger.debug("Done executing storage capacity update"); + s_logger.debug("Executing capacity updates for public ip and Vlans"); + } + + List datacenters = _dcDao.listAll(); + for (DataCenterVO datacenter : datacenters) { + long dcId = datacenter.getId(); + + //NOTE + //What happens if we have multiple vlans? Dashboard currently shows stats + //with no filter based on a vlan + //ideal way would be to remove out the vlan param, and filter only on dcId + //implementing the same + + // Calculate new Public IP capacity for Virtual Network + if (datacenter.getNetworkType() == NetworkType.Advanced){ + createOrUpdateIpCapacity(dcId, null, CapacityVO.CAPACITY_TYPE_VIRTUAL_NETWORK_PUBLIC_IP, datacenter.getAllocationState()); + } + + // Calculate new Public IP capacity for Direct Attached Network + createOrUpdateIpCapacity(dcId, null, CapacityVO.CAPACITY_TYPE_DIRECT_ATTACHED_PUBLIC_IP, datacenter.getAllocationState()); + + if (datacenter.getNetworkType() == NetworkType.Advanced){ + //Calculate VLAN's capacity + createOrUpdateVlanCapacity(dcId, datacenter.getAllocationState()); + } + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Done capacity updates for public ip and Vlans"); + s_logger.debug("Executing capacity updates for private ip"); + } + + // Calculate new Private IP capacity + List pods = _podDao.listAll(); + for (HostPodVO pod : pods) { + long podId = pod.getId(); + long dcId = pod.getDataCenterId(); + + createOrUpdateIpCapacity(dcId, podId, CapacityVO.CAPACITY_TYPE_PRIVATE_IP, _configMgr.findPodAllocationState(pod)); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Done executing capacity updates for private ip"); + s_logger.debug("Done recalculating system capacity"); + } } catch (Throwable t) { - s_logger.error("Caught exception in recalculating capacity", t); + s_logger.error("Caught exception in recalculating capacity", t); } } - - + + private void createOrUpdateVlanCapacity(long dcId, AllocationState capacityState) { - - SearchCriteria capacitySC = _capacityDao.createSearchCriteria(); + + SearchCriteria capacitySC = _capacityDao.createSearchCriteria(); List capacities = _capacityDao.search(capacitySC, null); capacitySC = _capacityDao.createSearchCriteria(); capacitySC.addAnd("dataCenterId", SearchCriteria.Op.EQ, dcId); capacitySC.addAnd("capacityType", SearchCriteria.Op.EQ, Capacity.CAPACITY_TYPE_VLAN); capacities = _capacityDao.search(capacitySC, null); - - int totalVlans = _dcDao.countZoneVlans(dcId, false); - int allocatedVlans = _dcDao.countZoneVlans(dcId, true); - + + int totalVlans = _dcDao.countZoneVlans(dcId, false); + int allocatedVlans = _dcDao.countZoneVlans(dcId, true); + if (capacities.size() == 0){ - CapacityVO newVlanCapacity = new CapacityVO(null, dcId, null, null, allocatedVlans, totalVlans, Capacity.CAPACITY_TYPE_VLAN); - if (capacityState == AllocationState.Disabled){ - newVlanCapacity.setCapacityState(CapacityState.Disabled); - } + CapacityVO newVlanCapacity = new CapacityVO(null, dcId, null, null, allocatedVlans, totalVlans, Capacity.CAPACITY_TYPE_VLAN); + if (capacityState == AllocationState.Disabled){ + newVlanCapacity.setCapacityState(CapacityState.Disabled); + } _capacityDao.persist(newVlanCapacity); }else if ( !(capacities.get(0).getUsedCapacity() == allocatedVlans - && capacities.get(0).getTotalCapacity() == totalVlans) ){ - CapacityVO capacity = capacities.get(0); - capacity.setUsedCapacity(allocatedVlans); - capacity.setTotalCapacity(totalVlans); + && capacities.get(0).getTotalCapacity() == totalVlans) ){ + CapacityVO capacity = capacities.get(0); + capacity.setUsedCapacity(allocatedVlans); + capacity.setTotalCapacity(totalVlans); _capacityDao.update(capacity.getId(), capacity); } - - - } - public void createOrUpdateIpCapacity(Long dcId, Long podId, short capacityType, AllocationState capacityState){ + + } + + public void createOrUpdateIpCapacity(Long dcId, Long podId, short capacityType, AllocationState capacityState){ SearchCriteria capacitySC = _capacityDao.createSearchCriteria(); List capacities = _capacityDao.search(capacitySC, null); @@ -401,55 +400,55 @@ public class AlertManagerImpl implements AlertManager { int allocatedIPs; capacities = _capacityDao.search(capacitySC, null); if (capacityType == CapacityVO.CAPACITY_TYPE_PRIVATE_IP){ - totalIPs = _privateIPAddressDao.countIPs(podId, dcId, false); - allocatedIPs = _privateIPAddressDao.countIPs(podId, dcId, true); + totalIPs = _privateIPAddressDao.countIPs(podId, dcId, false); + allocatedIPs = _privateIPAddressDao.countIPs(podId, dcId, true); }else if (capacityType == CapacityVO.CAPACITY_TYPE_VIRTUAL_NETWORK_PUBLIC_IP){ - totalIPs = _publicIPAddressDao.countIPsForNetwork(dcId, false, VlanType.VirtualNetwork); + totalIPs = _publicIPAddressDao.countIPsForNetwork(dcId, false, VlanType.VirtualNetwork); allocatedIPs = _publicIPAddressDao.countIPsForNetwork(dcId, true, VlanType.VirtualNetwork); }else { - totalIPs = _publicIPAddressDao.countIPsForNetwork(dcId, false, VlanType.DirectAttached); + totalIPs = _publicIPAddressDao.countIPsForNetwork(dcId, false, VlanType.DirectAttached); allocatedIPs = _publicIPAddressDao.countIPsForNetwork(dcId, true, VlanType.DirectAttached); } - + if (capacities.size() == 0){ - CapacityVO newPublicIPCapacity = new CapacityVO(null, dcId, podId, null, allocatedIPs, totalIPs, capacityType); - if (capacityState == AllocationState.Disabled){ - newPublicIPCapacity.setCapacityState(CapacityState.Disabled); - } + CapacityVO newPublicIPCapacity = new CapacityVO(null, dcId, podId, null, allocatedIPs, totalIPs, capacityType); + if (capacityState == AllocationState.Disabled){ + newPublicIPCapacity.setCapacityState(CapacityState.Disabled); + } _capacityDao.persist(newPublicIPCapacity); }else if ( !(capacities.get(0).getUsedCapacity() == allocatedIPs - && capacities.get(0).getTotalCapacity() == totalIPs) ){ - CapacityVO capacity = capacities.get(0); - capacity.setUsedCapacity(allocatedIPs); - capacity.setTotalCapacity(totalIPs); + && capacities.get(0).getTotalCapacity() == totalIPs) ){ + CapacityVO capacity = capacities.get(0); + capacity.setUsedCapacity(allocatedIPs); + capacity.setTotalCapacity(totalIPs); _capacityDao.update(capacity.getId(), capacity); } - + } class CapacityChecker extends TimerTask { @Override - public void run() { + public void run() { try { - s_logger.debug("Running Capacity Checker ... "); - checkForAlerts(); - s_logger.debug("Done running Capacity Checker ... "); + s_logger.debug("Running Capacity Checker ... "); + checkForAlerts(); + s_logger.debug("Done running Capacity Checker ... "); } catch (Throwable t) { s_logger.error("Exception in CapacityChecker", t); } } } - - + + public void checkForAlerts(){ - - recalculateCapacity(); + + recalculateCapacity(); // abort if we can't possibly send an alert... if (_emailAlert == null) { return; } - + //Get all datacenters, pods and clusters in the system. List dataCenterList = _dcDao.listAll(); List clusterList = _clusterDao.listAll(); @@ -458,89 +457,89 @@ public class AlertManagerImpl implements AlertManager { List dataCenterCapacityTypes = getCapacityTypesAtZoneLevel(); List podCapacityTypes = getCapacityTypesAtPodLevel(); List clusterCapacityTypes = getCapacityTypesAtClusterLevel(); - + // Generate Alerts for Zone Level capacities for(DataCenterVO dc : dataCenterList){ - for (Short capacityType : dataCenterCapacityTypes){ - List capacity = new ArrayList(); - capacity = _capacityDao.findCapacityBy(capacityType.intValue(), dc.getId(), null, null); - - if (capacityType == Capacity.CAPACITY_TYPE_SECONDARY_STORAGE){ - capacity.add(getUsedStats(capacityType, dc.getId(), null, null)); - } - if (capacity == null || capacity.size() == 0){ - continue; - } - double totalCapacity = capacity.get(0).getTotalCapacity(); + for (Short capacityType : dataCenterCapacityTypes){ + List capacity = new ArrayList(); + capacity = _capacityDao.findCapacityBy(capacityType.intValue(), dc.getId(), null, null); + + if (capacityType == Capacity.CAPACITY_TYPE_SECONDARY_STORAGE){ + capacity.add(getUsedStats(capacityType, dc.getId(), null, null)); + } + if (capacity == null || capacity.size() == 0){ + continue; + } + double totalCapacity = capacity.get(0).getTotalCapacity(); double usedCapacity = capacity.get(0).getUsedCapacity(); if (totalCapacity != 0 && usedCapacity/totalCapacity > _capacityTypeThresholdMap.get(capacityType)){ - generateEmailAlert(dc, null, null, totalCapacity, usedCapacity, capacityType); + generateEmailAlert(dc, null, null, totalCapacity, usedCapacity, capacityType); } - } + } } - + // Generate Alerts for Pod Level capacities for( HostPodVO pod : podList){ - for (Short capacityType : podCapacityTypes){ - List capacity = _capacityDao.findCapacityBy(capacityType.intValue(), pod.getDataCenterId(), pod.getId(), null); - if (capacity == null || capacity.size() == 0){ - continue; - } - double totalCapacity = capacity.get(0).getTotalCapacity(); + for (Short capacityType : podCapacityTypes){ + List capacity = _capacityDao.findCapacityBy(capacityType.intValue(), pod.getDataCenterId(), pod.getId(), null); + if (capacity == null || capacity.size() == 0){ + continue; + } + double totalCapacity = capacity.get(0).getTotalCapacity(); double usedCapacity = capacity.get(0).getUsedCapacity(); if (totalCapacity != 0 && usedCapacity/totalCapacity > _capacityTypeThresholdMap.get(capacityType)){ - generateEmailAlert(ApiDBUtils.findZoneById(pod.getDataCenterId()), pod, null, - totalCapacity, usedCapacity, capacityType); + generateEmailAlert(ApiDBUtils.findZoneById(pod.getDataCenterId()), pod, null, + totalCapacity, usedCapacity, capacityType); } - } + } } - + // Generate Alerts for Cluster Level capacities for( ClusterVO cluster : clusterList){ - for (Short capacityType : clusterCapacityTypes){ - List capacity = new ArrayList(); - float overProvFactor = 1f; - capacity = _capacityDao.findCapacityBy(capacityType.intValue(), cluster.getDataCenterId(), null, cluster.getId()); - - if (capacityType == Capacity.CAPACITY_TYPE_STORAGE){ - capacity.add(getUsedStats(capacityType, cluster.getDataCenterId(), cluster.getPodId(), cluster.getId())); - } - if (capacity == null || capacity.size() == 0){ - continue; - } - if (capacityType == Capacity.CAPACITY_TYPE_CPU){ - overProvFactor = ApiDBUtils.getCpuOverprovisioningFactor(); - } - - double totalCapacity = capacity.get(0).getTotalCapacity() * overProvFactor; + for (Short capacityType : clusterCapacityTypes){ + List capacity = new ArrayList(); + float overProvFactor = 1f; + capacity = _capacityDao.findCapacityBy(capacityType.intValue(), cluster.getDataCenterId(), null, cluster.getId()); + + if (capacityType == Capacity.CAPACITY_TYPE_STORAGE){ + capacity.add(getUsedStats(capacityType, cluster.getDataCenterId(), cluster.getPodId(), cluster.getId())); + } + if (capacity == null || capacity.size() == 0){ + continue; + } + if (capacityType == Capacity.CAPACITY_TYPE_CPU){ + overProvFactor = ApiDBUtils.getCpuOverprovisioningFactor(); + } + + double totalCapacity = capacity.get(0).getTotalCapacity() * overProvFactor; double usedCapacity = capacity.get(0).getUsedCapacity() + capacity.get(0).getReservedCapacity(); if (totalCapacity != 0 && usedCapacity/totalCapacity > _capacityTypeThresholdMap.get(capacityType)){ - generateEmailAlert(ApiDBUtils.findZoneById(cluster.getDataCenterId()), ApiDBUtils.findPodById(cluster.getPodId()), cluster, - totalCapacity, usedCapacity, capacityType); + generateEmailAlert(ApiDBUtils.findZoneById(cluster.getDataCenterId()), ApiDBUtils.findPodById(cluster.getPodId()), cluster, + totalCapacity, usedCapacity, capacityType); } - } + } } - + } - + private SummedCapacity getUsedStats(short capacityType, long zoneId, Long podId, Long clusterId){ - CapacityVO capacity; - if (capacityType == Capacity.CAPACITY_TYPE_SECONDARY_STORAGE){ - capacity = _storageMgr.getSecondaryStorageUsedStats(null, zoneId); - }else{ - capacity = _storageMgr.getStoragePoolUsedStats(null, clusterId, podId, zoneId); - } - if (capacity != null){ - return new SummedCapacity(capacity.getUsedCapacity(), 0, capacity.getTotalCapacity(), capacityType, clusterId, podId); - }else{ - return null; - } - + CapacityVO capacity; + if (capacityType == Capacity.CAPACITY_TYPE_SECONDARY_STORAGE){ + capacity = _storageMgr.getSecondaryStorageUsedStats(null, zoneId); + }else{ + capacity = _storageMgr.getStoragePoolUsedStats(null, clusterId, podId, zoneId); + } + if (capacity != null){ + return new SummedCapacity(capacity.getUsedCapacity(), 0, capacity.getTotalCapacity(), capacityType, clusterId, podId); + }else{ + return null; + } + } - + private void generateEmailAlert(DataCenterVO dc, HostPodVO pod, ClusterVO cluster, double totalCapacity, double usedCapacity, short capacityType){ - - String msgSubject = null; + + String msgSubject = null; String msgContent = null; String totalStr; String usedStr; @@ -548,10 +547,10 @@ public class AlertManagerImpl implements AlertManager { short alertType = -1; Long podId = pod == null ? null : pod.getId(); Long clusterId = cluster == null ? null : cluster.getId(); - - switch (capacityType) { - - //Cluster Level + + switch (capacityType) { + + //Cluster Level case CapacityVO.CAPACITY_TYPE_MEMORY: msgSubject = "System Alert: Low Available Memory in cluster " +cluster.getName()+ " pod " +pod.getName()+ " of availability zone " + dc.getName(); totalStr = formatBytesToMegabytes(totalCapacity); @@ -587,24 +586,24 @@ public class AlertManagerImpl implements AlertManager { msgContent = "Unallocated storage space is low, total: " + totalStr + " MB, allocated: " + usedStr + " MB (" + pctStr + "%)"; alertType = ALERT_TYPE_LOCAL_STORAGE; break; - - //Pod Level + + //Pod Level case CapacityVO.CAPACITY_TYPE_PRIVATE_IP: - msgSubject = "System Alert: Number of unallocated private IPs is low in pod " +pod.getName()+ " of availability zone " + dc.getName(); - totalStr = Double.toString(totalCapacity); + msgSubject = "System Alert: Number of unallocated private IPs is low in pod " +pod.getName()+ " of availability zone " + dc.getName(); + totalStr = Double.toString(totalCapacity); usedStr = Double.toString(usedCapacity); - msgContent = "Number of unallocated private IPs is low, total: " + totalStr + ", allocated: " + usedStr + " (" + pctStr + "%)"; - alertType = ALERT_TYPE_PRIVATE_IP; - break; - - //Zone Level + msgContent = "Number of unallocated private IPs is low, total: " + totalStr + ", allocated: " + usedStr + " (" + pctStr + "%)"; + alertType = ALERT_TYPE_PRIVATE_IP; + break; + + //Zone Level case CapacityVO.CAPACITY_TYPE_SECONDARY_STORAGE: - msgSubject = "System Alert: Low Available Secondary Storage in availability zone " + dc.getName(); - totalStr = formatBytesToMegabytes(totalCapacity); + msgSubject = "System Alert: Low Available Secondary Storage in availability zone " + dc.getName(); + totalStr = formatBytesToMegabytes(totalCapacity); usedStr = formatBytesToMegabytes(usedCapacity); - msgContent = "Available secondary storage space is low, total: " + totalStr + " MB, used: " + usedStr + " MB (" + pctStr + "%)"; - alertType = ALERT_TYPE_SECONDARY_STORAGE; - break; + msgContent = "Available secondary storage space is low, total: " + totalStr + " MB, used: " + usedStr + " MB (" + pctStr + "%)"; + alertType = ALERT_TYPE_SECONDARY_STORAGE; + break; case CapacityVO.CAPACITY_TYPE_VIRTUAL_NETWORK_PUBLIC_IP: msgSubject = "System Alert: Number of unallocated virtual network public IPs is low in availability zone " + dc.getName(); totalStr = Double.toString(totalCapacity); @@ -627,47 +626,47 @@ public class AlertManagerImpl implements AlertManager { alertType = ALERT_TYPE_VLAN; break; } - - try { - if (s_logger.isDebugEnabled()){ - s_logger.debug(msgSubject); - s_logger.debug(msgContent); - } - _emailAlert.sendAlert(alertType, dc.getId(), podId, clusterId, msgSubject, msgContent); - } catch (Exception ex) { + + try { + if (s_logger.isDebugEnabled()){ + s_logger.debug(msgSubject); + s_logger.debug(msgContent); + } + _emailAlert.sendAlert(alertType, dc.getId(), podId, clusterId, msgSubject, msgContent); + } catch (Exception ex) { s_logger.error("Exception in CapacityChecker", ex); - } + } } - + private List getCapacityTypesAtZoneLevel(){ - - List dataCenterCapacityTypes = new ArrayList(); - dataCenterCapacityTypes.add(Capacity.CAPACITY_TYPE_VIRTUAL_NETWORK_PUBLIC_IP); - dataCenterCapacityTypes.add(Capacity.CAPACITY_TYPE_DIRECT_ATTACHED_PUBLIC_IP); - dataCenterCapacityTypes.add(Capacity.CAPACITY_TYPE_SECONDARY_STORAGE); - dataCenterCapacityTypes.add(Capacity.CAPACITY_TYPE_VLAN); - return dataCenterCapacityTypes; - + + List dataCenterCapacityTypes = new ArrayList(); + dataCenterCapacityTypes.add(Capacity.CAPACITY_TYPE_VIRTUAL_NETWORK_PUBLIC_IP); + dataCenterCapacityTypes.add(Capacity.CAPACITY_TYPE_DIRECT_ATTACHED_PUBLIC_IP); + dataCenterCapacityTypes.add(Capacity.CAPACITY_TYPE_SECONDARY_STORAGE); + dataCenterCapacityTypes.add(Capacity.CAPACITY_TYPE_VLAN); + return dataCenterCapacityTypes; + } - + private List getCapacityTypesAtPodLevel(){ - - List podCapacityTypes = new ArrayList(); - podCapacityTypes.add(Capacity.CAPACITY_TYPE_PRIVATE_IP); - return podCapacityTypes; - + + List podCapacityTypes = new ArrayList(); + podCapacityTypes.add(Capacity.CAPACITY_TYPE_PRIVATE_IP); + return podCapacityTypes; + } - + private List getCapacityTypesAtClusterLevel(){ - - List clusterCapacityTypes = new ArrayList(); - clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_CPU); - clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_MEMORY); - clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_STORAGE); - clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED); - clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_LOCAL_STORAGE); - return clusterCapacityTypes; - + + List clusterCapacityTypes = new ArrayList(); + clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_CPU); + clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_MEMORY); + clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_STORAGE); + clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED); + clusterCapacityTypes.add(Capacity.CAPACITY_TYPE_LOCAL_STORAGE); + return clusterCapacityTypes; + } class EmailAlert { @@ -735,12 +734,12 @@ public class AlertManagerImpl implements AlertManager { public void sendAlert(short alertType, long dataCenterId, Long podId, Long clusterId, String subject, String content) throws MessagingException, UnsupportedEncodingException { AlertVO alert = null; if ((alertType != AlertManager.ALERT_TYPE_HOST) && - (alertType != AlertManager.ALERT_TYPE_USERVM) && - (alertType != AlertManager.ALERT_TYPE_DOMAIN_ROUTER) && - (alertType != AlertManager.ALERT_TYPE_CONSOLE_PROXY) && - (alertType != AlertManager.ALERT_TYPE_SSVM) && - (alertType != AlertManager.ALERT_TYPE_STORAGE_MISC) && - (alertType != AlertManager.ALERT_TYPE_MANAGMENT_NODE)) { + (alertType != AlertManager.ALERT_TYPE_USERVM) && + (alertType != AlertManager.ALERT_TYPE_DOMAIN_ROUTER) && + (alertType != AlertManager.ALERT_TYPE_CONSOLE_PROXY) && + (alertType != AlertManager.ALERT_TYPE_SSVM) && + (alertType != AlertManager.ALERT_TYPE_STORAGE_MISC) && + (alertType != AlertManager.ALERT_TYPE_MANAGMENT_NODE)) { alert = _alertDao.getLastAlert(alertType, dataCenterId, podId, clusterId); } diff --git a/server/src/com/cloud/alert/ConsoleProxyAlertAdapter.java b/server/src/com/cloud/alert/ConsoleProxyAlertAdapter.java index 37e385021e1..7e744229bf7 100644 --- a/server/src/com/cloud/alert/ConsoleProxyAlertAdapter.java +++ b/server/src/com/cloud/alert/ConsoleProxyAlertAdapter.java @@ -25,13 +25,10 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; -import com.cloud.alert.AlertAdapter; -import com.cloud.alert.AlertManager; import com.cloud.consoleproxy.ConsoleProxyAlertEventArgs; import com.cloud.consoleproxy.ConsoleProxyManager; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.events.SubscriptionMgr; import com.cloud.vm.ConsoleProxyVO; import com.cloud.vm.dao.ConsoleProxyDao; @@ -39,178 +36,178 @@ import com.cloud.vm.dao.ConsoleProxyDao; @Component @Local(value=AlertAdapter.class) public class ConsoleProxyAlertAdapter implements AlertAdapter { - - private static final Logger s_logger = Logger.getLogger(ConsoleProxyAlertAdapter.class); - - @Inject private AlertManager _alertMgr; + + private static final Logger s_logger = Logger.getLogger(ConsoleProxyAlertAdapter.class); + + @Inject private AlertManager _alertMgr; private String _name; - - @Inject private DataCenterDao _dcDao; - @Inject private ConsoleProxyDao _consoleProxyDao; - + + @Inject private DataCenterDao _dcDao; + @Inject private ConsoleProxyDao _consoleProxyDao; + public void onProxyAlert(Object sender, ConsoleProxyAlertEventArgs args) { - if(s_logger.isDebugEnabled()) - s_logger.debug("received console proxy alert"); - - DataCenterVO dc = _dcDao.findById(args.getZoneId()); - ConsoleProxyVO proxy = args.getProxy(); - if(proxy == null) - proxy = _consoleProxyDao.findById(args.getProxyId()); - - switch(args.getType()) { - case ConsoleProxyAlertEventArgs.PROXY_CREATED : - if(s_logger.isDebugEnabled()) - s_logger.debug("New console proxy created, zone: " + dc.getName() + ", proxy: " + - proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + - proxy.getPrivateIpAddress()); - break; - - case ConsoleProxyAlertEventArgs.PROXY_UP : - if(s_logger.isDebugEnabled()) - s_logger.debug("Console proxy is up, zone: " + dc.getName() + ", proxy: " + - proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + - proxy.getPrivateIpAddress()); - - _alertMgr.sendAlert( - AlertManager.ALERT_TYPE_CONSOLE_PROXY, - args.getZoneId(), - proxy.getPodIdToDeployIn(), - "Console proxy up in zone: " + dc.getName() + ", proxy: " + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() - + ", private IP: " + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress()), - "Console proxy up (zone " + dc.getName() + ")" - ); - break; - - case ConsoleProxyAlertEventArgs.PROXY_DOWN : - if(s_logger.isDebugEnabled()) - s_logger.debug("Console proxy is down, zone: " + dc.getName() + ", proxy: " + - proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + - (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress())); - - _alertMgr.sendAlert( - AlertManager.ALERT_TYPE_CONSOLE_PROXY, - args.getZoneId(), - proxy.getPodIdToDeployIn(), - "Console proxy down in zone: " + dc.getName() + ", proxy: " + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() - + ", private IP: " + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress()), - "Console proxy down (zone " + dc.getName() + ")" - ); - break; - - case ConsoleProxyAlertEventArgs.PROXY_REBOOTED : - if(s_logger.isDebugEnabled()) - s_logger.debug("Console proxy is rebooted, zone: " + dc.getName() + ", proxy: " + - proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + - (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress())); - - _alertMgr.sendAlert( - AlertManager.ALERT_TYPE_CONSOLE_PROXY, - args.getZoneId(), - proxy.getPodIdToDeployIn(), - "Console proxy rebooted in zone: " + dc.getName() + ", proxy: " + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() - + ", private IP: " + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress()), - "Console proxy rebooted (zone " + dc.getName() + ")" - ); - break; - - case ConsoleProxyAlertEventArgs.PROXY_CREATE_FAILURE : - if(s_logger.isDebugEnabled()) - s_logger.debug("Console proxy creation failure, zone: " + dc.getName() + ", proxy: " + - proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + - (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress())); - - _alertMgr.sendAlert( - AlertManager.ALERT_TYPE_CONSOLE_PROXY, - args.getZoneId(), - proxy.getPodIdToDeployIn(), - "Console proxy creation failure. zone: " + dc.getName() + ", proxy: " + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() - + ", private IP: " + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress()) - + ", error details: " + args.getMessage(), - "Console proxy creation failure (zone " + dc.getName() + ")" - ); - break; - - case ConsoleProxyAlertEventArgs.PROXY_START_FAILURE : - if(s_logger.isDebugEnabled()) - s_logger.debug("Console proxy startup failure, zone: " + dc.getName() + ", proxy: " + - proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + - (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress())); - - _alertMgr.sendAlert( - AlertManager.ALERT_TYPE_CONSOLE_PROXY, - args.getZoneId(), - proxy.getPodIdToDeployIn(), - "Console proxy startup failure. zone: " + dc.getName() + ", proxy: " + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() - + ", private IP: " + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress()) - + ", error details: " + args.getMessage(), - "Console proxy startup failure (zone " + dc.getName() + ")" - ); - break; - - case ConsoleProxyAlertEventArgs.PROXY_FIREWALL_ALERT : - if(s_logger.isDebugEnabled()) - s_logger.debug("Console proxy firewall alert, zone: " + dc.getName() + ", proxy: " + - proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + - (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress())); - - _alertMgr.sendAlert( - AlertManager.ALERT_TYPE_CONSOLE_PROXY, - args.getZoneId(), - proxy.getPodIdToDeployIn(), - "Failed to open console proxy firewall port. zone: " + dc.getName() + ", proxy: " + proxy.getHostName() - + ", public IP: " + proxy.getPublicIpAddress() - + ", private IP: " + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress()), - "Console proxy alert (zone " + dc.getName() + ")" - ); - break; - - case ConsoleProxyAlertEventArgs.PROXY_STORAGE_ALERT : - if(s_logger.isDebugEnabled()) - s_logger.debug("Console proxy storage alert, zone: " + dc.getName() + ", proxy: " + - proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + - proxy.getPrivateIpAddress() + ", message: " + args.getMessage()); - - _alertMgr.sendAlert( - AlertManager.ALERT_TYPE_STORAGE_MISC, - args.getZoneId(), - proxy.getPodIdToDeployIn(), - "Console proxy storage issue. zone: " + dc.getName() + ", message: " + args.getMessage(), - "Console proxy alert (zone " + dc.getName() + ")" - ); - break; - } + if(s_logger.isDebugEnabled()) + s_logger.debug("received console proxy alert"); + + DataCenterVO dc = _dcDao.findById(args.getZoneId()); + ConsoleProxyVO proxy = args.getProxy(); + if(proxy == null) + proxy = _consoleProxyDao.findById(args.getProxyId()); + + switch(args.getType()) { + case ConsoleProxyAlertEventArgs.PROXY_CREATED : + if(s_logger.isDebugEnabled()) + s_logger.debug("New console proxy created, zone: " + dc.getName() + ", proxy: " + + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + + proxy.getPrivateIpAddress()); + break; + + case ConsoleProxyAlertEventArgs.PROXY_UP : + if(s_logger.isDebugEnabled()) + s_logger.debug("Console proxy is up, zone: " + dc.getName() + ", proxy: " + + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + + proxy.getPrivateIpAddress()); + + _alertMgr.sendAlert( + AlertManager.ALERT_TYPE_CONSOLE_PROXY, + args.getZoneId(), + proxy.getPodIdToDeployIn(), + "Console proxy up in zone: " + dc.getName() + ", proxy: " + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + + ", private IP: " + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress()), + "Console proxy up (zone " + dc.getName() + ")" + ); + break; + + case ConsoleProxyAlertEventArgs.PROXY_DOWN : + if(s_logger.isDebugEnabled()) + s_logger.debug("Console proxy is down, zone: " + dc.getName() + ", proxy: " + + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress())); + + _alertMgr.sendAlert( + AlertManager.ALERT_TYPE_CONSOLE_PROXY, + args.getZoneId(), + proxy.getPodIdToDeployIn(), + "Console proxy down in zone: " + dc.getName() + ", proxy: " + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + + ", private IP: " + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress()), + "Console proxy down (zone " + dc.getName() + ")" + ); + break; + + case ConsoleProxyAlertEventArgs.PROXY_REBOOTED : + if(s_logger.isDebugEnabled()) + s_logger.debug("Console proxy is rebooted, zone: " + dc.getName() + ", proxy: " + + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress())); + + _alertMgr.sendAlert( + AlertManager.ALERT_TYPE_CONSOLE_PROXY, + args.getZoneId(), + proxy.getPodIdToDeployIn(), + "Console proxy rebooted in zone: " + dc.getName() + ", proxy: " + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + + ", private IP: " + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress()), + "Console proxy rebooted (zone " + dc.getName() + ")" + ); + break; + + case ConsoleProxyAlertEventArgs.PROXY_CREATE_FAILURE : + if(s_logger.isDebugEnabled()) + s_logger.debug("Console proxy creation failure, zone: " + dc.getName() + ", proxy: " + + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress())); + + _alertMgr.sendAlert( + AlertManager.ALERT_TYPE_CONSOLE_PROXY, + args.getZoneId(), + proxy.getPodIdToDeployIn(), + "Console proxy creation failure. zone: " + dc.getName() + ", proxy: " + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + + ", private IP: " + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress()) + + ", error details: " + args.getMessage(), + "Console proxy creation failure (zone " + dc.getName() + ")" + ); + break; + + case ConsoleProxyAlertEventArgs.PROXY_START_FAILURE : + if(s_logger.isDebugEnabled()) + s_logger.debug("Console proxy startup failure, zone: " + dc.getName() + ", proxy: " + + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress())); + + _alertMgr.sendAlert( + AlertManager.ALERT_TYPE_CONSOLE_PROXY, + args.getZoneId(), + proxy.getPodIdToDeployIn(), + "Console proxy startup failure. zone: " + dc.getName() + ", proxy: " + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + + ", private IP: " + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress()) + + ", error details: " + args.getMessage(), + "Console proxy startup failure (zone " + dc.getName() + ")" + ); + break; + + case ConsoleProxyAlertEventArgs.PROXY_FIREWALL_ALERT : + if(s_logger.isDebugEnabled()) + s_logger.debug("Console proxy firewall alert, zone: " + dc.getName() + ", proxy: " + + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress())); + + _alertMgr.sendAlert( + AlertManager.ALERT_TYPE_CONSOLE_PROXY, + args.getZoneId(), + proxy.getPodIdToDeployIn(), + "Failed to open console proxy firewall port. zone: " + dc.getName() + ", proxy: " + proxy.getHostName() + + ", public IP: " + proxy.getPublicIpAddress() + + ", private IP: " + (proxy.getPrivateIpAddress() == null ? "N/A" : proxy.getPrivateIpAddress()), + "Console proxy alert (zone " + dc.getName() + ")" + ); + break; + + case ConsoleProxyAlertEventArgs.PROXY_STORAGE_ALERT : + if(s_logger.isDebugEnabled()) + s_logger.debug("Console proxy storage alert, zone: " + dc.getName() + ", proxy: " + + proxy.getHostName() + ", public IP: " + proxy.getPublicIpAddress() + ", private IP: " + + proxy.getPrivateIpAddress() + ", message: " + args.getMessage()); + + _alertMgr.sendAlert( + AlertManager.ALERT_TYPE_STORAGE_MISC, + args.getZoneId(), + proxy.getPodIdToDeployIn(), + "Console proxy storage issue. zone: " + dc.getName() + ", message: " + args.getMessage(), + "Console proxy alert (zone " + dc.getName() + ")" + ); + break; + } } - - @Override - public boolean configure(String name, Map params) - throws ConfigurationException { - - if (s_logger.isInfoEnabled()) - s_logger.info("Start configuring console proxy alert manager : " + name); - try { - SubscriptionMgr.getInstance().subscribe(ConsoleProxyManager.ALERT_SUBJECT, this, "onProxyAlert"); - } catch (SecurityException e) { - throw new ConfigurationException("Unable to register console proxy event subscription, exception: " + e); - } catch (NoSuchMethodException e) { - throw new ConfigurationException("Unable to register console proxy event subscription, exception: " + e); - } - - return true; - } + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { - @Override - public String getName() { - return _name; - } + if (s_logger.isInfoEnabled()) + s_logger.info("Start configuring console proxy alert manager : " + name); - @Override - public boolean start() { - return true; - } + try { + SubscriptionMgr.getInstance().subscribe(ConsoleProxyManager.ALERT_SUBJECT, this, "onProxyAlert"); + } catch (SecurityException e) { + throw new ConfigurationException("Unable to register console proxy event subscription, exception: " + e); + } catch (NoSuchMethodException e) { + throw new ConfigurationException("Unable to register console proxy event subscription, exception: " + e); + } - @Override - public boolean stop() { - return true; - } + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } } diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java index 5e8a044691d..8a15dd12a7d 100755 --- a/server/src/com/cloud/api/ApiDBUtils.java +++ b/server/src/com/cloud/api/ApiDBUtils.java @@ -22,6 +22,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import javax.inject.Inject; + import org.apache.cloudstack.api.ApiConstants.HostDetails; import org.apache.cloudstack.api.ApiConstants.VMDetails; import org.apache.cloudstack.api.response.AccountResponse; @@ -114,11 +116,11 @@ import com.cloud.network.NetworkManager; import com.cloud.network.NetworkProfile; import com.cloud.network.NetworkRuleConfigVO; import com.cloud.network.NetworkVO; +import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetworkServiceProvider; import com.cloud.network.PhysicalNetworkVO; -import com.cloud.network.Site2SiteVpnGatewayVO; import com.cloud.network.Site2SiteCustomerGatewayVO; -import com.cloud.network.Networks.TrafficType; +import com.cloud.network.Site2SiteVpnGatewayVO; import com.cloud.network.as.AutoScalePolicy; import com.cloud.network.as.AutoScalePolicyConditionMapVO; import com.cloud.network.as.AutoScalePolicyVO; @@ -139,15 +141,15 @@ import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.LoadBalancerDao; import com.cloud.network.dao.NetworkDao; -import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.NetworkDomainDao; import com.cloud.network.dao.NetworkRuleConfigDao; +import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao; import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO; -import com.cloud.network.dao.Site2SiteVpnGatewayDao; import com.cloud.network.dao.Site2SiteCustomerGatewayDao; +import com.cloud.network.dao.Site2SiteVpnGatewayDao; import com.cloud.network.router.VirtualRouter; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.network.security.SecurityGroup; @@ -160,6 +162,7 @@ import com.cloud.network.vpc.VpcManager; import com.cloud.network.vpc.VpcOffering; import com.cloud.network.vpc.VpcVO; import com.cloud.network.vpc.dao.StaticRouteDao; +import com.cloud.network.vpc.dao.VpcDao; import com.cloud.network.vpc.dao.VpcGatewayDao; import com.cloud.network.vpc.dao.VpcOfferingDao; import com.cloud.offering.NetworkOffering; @@ -229,7 +232,6 @@ import com.cloud.user.dao.UserStatisticsDao; import com.cloud.uservm.UserVm; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; import com.cloud.vm.ConsoleProxyVO; import com.cloud.vm.DomainRouterVO; import com.cloud.vm.InstanceGroup; @@ -246,194 +248,102 @@ import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; -import com.cloud.network.vpc.dao.VpcDao; public class ApiDBUtils { private static ManagementServer _ms; - public static AsyncJobManager _asyncMgr; - private static SecurityGroupManager _securityGroupMgr; - private static StorageManager _storageMgr; - private static UserVmManager _userVmMgr; - private static NetworkManager _networkMgr; - private static StatsCollector _statsCollector; + @Inject static AsyncJobManager _asyncMgr; + @Inject static SecurityGroupManager _securityGroupMgr; + @Inject static StorageManager _storageMgr; + @Inject static UserVmManager _userVmMgr; + @Inject static NetworkManager _networkMgr; + @Inject static StatsCollector _statsCollector; - private static AccountDao _accountDao; - private static AccountVlanMapDao _accountVlanMapDao; - private static ClusterDao _clusterDao; - private static CapacityDao _capacityDao; - private static DiskOfferingDao _diskOfferingDao; - private static DomainDao _domainDao; - private static DomainRouterDao _domainRouterDao; - private static DomainRouterJoinDao _domainRouterJoinDao; - private static GuestOSDao _guestOSDao; - private static GuestOSCategoryDao _guestOSCategoryDao; - private static HostDao _hostDao; - private static IPAddressDao _ipAddressDao; - private static LoadBalancerDao _loadBalancerDao; - private static SecurityGroupDao _securityGroupDao; - private static SecurityGroupJoinDao _securityGroupJoinDao; - private static NetworkRuleConfigDao _networkRuleConfigDao; - private static HostPodDao _podDao; - private static ServiceOfferingDao _serviceOfferingDao; - private static SnapshotDao _snapshotDao; - private static StoragePoolDao _storagePoolDao; - private static VMTemplateDao _templateDao; - private static VMTemplateDetailsDao _templateDetailsDao; - private static VMTemplateHostDao _templateHostDao; - private static VMTemplateSwiftDao _templateSwiftDao; - private static VMTemplateS3Dao _templateS3Dao; - private static UploadDao _uploadDao; - private static UserDao _userDao; - private static UserStatisticsDao _userStatsDao; - private static UserVmDao _userVmDao; - private static UserVmJoinDao _userVmJoinDao; - private static VlanDao _vlanDao; - private static VolumeDao _volumeDao; - private static Site2SiteVpnGatewayDao _site2SiteVpnGatewayDao; - private static Site2SiteCustomerGatewayDao _site2SiteCustomerGatewayDao; - private static VolumeHostDao _volumeHostDao; - private static DataCenterDao _zoneDao; - private static NetworkOfferingDao _networkOfferingDao; - private static NetworkDao _networkDao; - private static PhysicalNetworkDao _physicalNetworkDao; - private static ConfigurationService _configMgr; - private static ConfigurationDao _configDao; - private static ConsoleProxyDao _consoleProxyDao; - private static FirewallRulesCidrsDao _firewallCidrsDao; - private static VMInstanceDao _vmDao; - private static ResourceLimitService _resourceLimitMgr; - private static ProjectService _projectMgr; - private static ResourceManager _resourceMgr; - private static AccountDetailsDao _accountDetailsDao; - private static NetworkDomainDao _networkDomainDao; - private static HighAvailabilityManager _haMgr; - private static VpcManager _vpcMgr; - private static TaggedResourceService _taggedResourceService; - private static UserVmDetailsDao _userVmDetailsDao; - private static SSHKeyPairDao _sshKeyPairDao; + @Inject static AccountDao _accountDao; + @Inject static AccountVlanMapDao _accountVlanMapDao; + @Inject static ClusterDao _clusterDao; + @Inject static CapacityDao _capacityDao; + @Inject static DiskOfferingDao _diskOfferingDao; + @Inject static DomainDao _domainDao; + @Inject static DomainRouterDao _domainRouterDao; + @Inject static DomainRouterJoinDao _domainRouterJoinDao; + @Inject static GuestOSDao _guestOSDao; + @Inject static GuestOSCategoryDao _guestOSCategoryDao; + @Inject static HostDao _hostDao; + @Inject static IPAddressDao _ipAddressDao; + @Inject static LoadBalancerDao _loadBalancerDao; + @Inject static SecurityGroupDao _securityGroupDao; + @Inject static SecurityGroupJoinDao _securityGroupJoinDao; + @Inject static NetworkRuleConfigDao _networkRuleConfigDao; + @Inject static HostPodDao _podDao; + @Inject static ServiceOfferingDao _serviceOfferingDao; + @Inject static SnapshotDao _snapshotDao; + @Inject static StoragePoolDao _storagePoolDao; + @Inject static VMTemplateDao _templateDao; + @Inject static VMTemplateDetailsDao _templateDetailsDao; + @Inject static VMTemplateHostDao _templateHostDao; + @Inject static VMTemplateSwiftDao _templateSwiftDao; + @Inject static VMTemplateS3Dao _templateS3Dao; + @Inject static UploadDao _uploadDao; + @Inject static UserDao _userDao; + @Inject static UserStatisticsDao _userStatsDao; + @Inject static UserVmDao _userVmDao; + @Inject static UserVmJoinDao _userVmJoinDao; + @Inject static VlanDao _vlanDao; + @Inject static VolumeDao _volumeDao; + @Inject static Site2SiteVpnGatewayDao _site2SiteVpnGatewayDao; + @Inject static Site2SiteCustomerGatewayDao _site2SiteCustomerGatewayDao; + @Inject static VolumeHostDao _volumeHostDao; + @Inject static DataCenterDao _zoneDao; + @Inject static NetworkOfferingDao _networkOfferingDao; + @Inject static NetworkDao _networkDao; + @Inject static PhysicalNetworkDao _physicalNetworkDao; + @Inject static ConfigurationService _configMgr; + @Inject static ConfigurationDao _configDao; + @Inject static ConsoleProxyDao _consoleProxyDao; + @Inject static FirewallRulesCidrsDao _firewallCidrsDao; + @Inject static VMInstanceDao _vmDao; + @Inject static ResourceLimitService _resourceLimitMgr; + @Inject static ProjectService _projectMgr; + @Inject static ResourceManager _resourceMgr; + @Inject static AccountDetailsDao _accountDetailsDao; + @Inject static NetworkDomainDao _networkDomainDao; + @Inject static HighAvailabilityManager _haMgr; + @Inject static VpcManager _vpcMgr; + @Inject static TaggedResourceService _taggedResourceService; + @Inject static UserVmDetailsDao _userVmDetailsDao; + @Inject static SSHKeyPairDao _sshKeyPairDao; - private static ConditionDao _asConditionDao; - private static AutoScalePolicyConditionMapDao _asPolicyConditionMapDao; - private static AutoScaleVmGroupPolicyMapDao _asVmGroupPolicyMapDao; - private static AutoScalePolicyDao _asPolicyDao; - private static AutoScaleVmProfileDao _asVmProfileDao; - private static AutoScaleVmGroupDao _asVmGroupDao; - private static CounterDao _counterDao; - private static ResourceTagJoinDao _tagJoinDao; - private static EventJoinDao _eventJoinDao; - private static InstanceGroupJoinDao _vmGroupJoinDao; - private static UserAccountJoinDao _userAccountJoinDao; - private static ProjectJoinDao _projectJoinDao; - private static ProjectAccountJoinDao _projectAccountJoinDao; - private static ProjectInvitationJoinDao _projectInvitationJoinDao; - private static HostJoinDao _hostJoinDao; - private static VolumeJoinDao _volJoinDao; - private static StoragePoolJoinDao _poolJoinDao; - private static AccountJoinDao _accountJoinDao; - private static AsyncJobJoinDao _jobJoinDao; + @Inject static ConditionDao _asConditionDao; + @Inject static AutoScalePolicyConditionMapDao _asPolicyConditionMapDao; + @Inject static AutoScaleVmGroupPolicyMapDao _asVmGroupPolicyMapDao; + @Inject static AutoScalePolicyDao _asPolicyDao; + @Inject static AutoScaleVmProfileDao _asVmProfileDao; + @Inject static AutoScaleVmGroupDao _asVmGroupDao; + @Inject static CounterDao _counterDao; + @Inject static ResourceTagJoinDao _tagJoinDao; + @Inject static EventJoinDao _eventJoinDao; + @Inject static InstanceGroupJoinDao _vmGroupJoinDao; + @Inject static UserAccountJoinDao _userAccountJoinDao; + @Inject static ProjectJoinDao _projectJoinDao; + @Inject static ProjectAccountJoinDao _projectAccountJoinDao; + @Inject static ProjectInvitationJoinDao _projectInvitationJoinDao; + @Inject static HostJoinDao _hostJoinDao; + @Inject static VolumeJoinDao _volJoinDao; + @Inject static StoragePoolJoinDao _poolJoinDao; + @Inject static AccountJoinDao _accountJoinDao; + @Inject static AsyncJobJoinDao _jobJoinDao; - private static PhysicalNetworkTrafficTypeDao _physicalNetworkTrafficTypeDao; - private static PhysicalNetworkServiceProviderDao _physicalNetworkServiceProviderDao; - private static FirewallRulesDao _firewallRuleDao; - private static StaticRouteDao _staticRouteDao; - private static VpcGatewayDao _vpcGatewayDao; - private static VpcDao _vpcDao; - private static VpcOfferingDao _vpcOfferingDao; - private static SnapshotPolicyDao _snapshotPolicyDao; - private static AsyncJobDao _asyncJobDao; + @Inject static PhysicalNetworkTrafficTypeDao _physicalNetworkTrafficTypeDao; + @Inject static PhysicalNetworkServiceProviderDao _physicalNetworkServiceProviderDao; + @Inject static FirewallRulesDao _firewallRuleDao; + @Inject static StaticRouteDao _staticRouteDao; + @Inject static VpcGatewayDao _vpcGatewayDao; + @Inject static VpcDao _vpcDao; + @Inject static VpcOfferingDao _vpcOfferingDao; + @Inject static SnapshotPolicyDao _snapshotPolicyDao; + @Inject static AsyncJobDao _asyncJobDao; static { - _ms = (ManagementServer) ComponentLocator.getComponent(ManagementServer.Name); - ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); - _asyncMgr = locator.getManager(AsyncJobManager.class); - _securityGroupMgr = locator.getManager(SecurityGroupManager.class); - _storageMgr = locator.getManager(StorageManager.class); - _userVmMgr = locator.getManager(UserVmManager.class); - _networkMgr = locator.getManager(NetworkManager.class); - _configMgr = locator.getManager(ConfigurationService.class); - - _accountDao = locator.getDao(AccountDao.class); - _accountVlanMapDao = locator.getDao(AccountVlanMapDao.class); - _clusterDao = locator.getDao(ClusterDao.class); - _capacityDao = locator.getDao(CapacityDao.class); - _diskOfferingDao = locator.getDao(DiskOfferingDao.class); - _domainDao = locator.getDao(DomainDao.class); - _domainRouterDao = locator.getDao(DomainRouterDao.class); - _domainRouterJoinDao = locator.getDao(DomainRouterJoinDao.class); - _guestOSDao = locator.getDao(GuestOSDao.class); - _guestOSCategoryDao = locator.getDao(GuestOSCategoryDao.class); - _hostDao = locator.getDao(HostDao.class); - _ipAddressDao = locator.getDao(IPAddressDao.class); - _loadBalancerDao = locator.getDao(LoadBalancerDao.class); - _networkRuleConfigDao = locator.getDao(NetworkRuleConfigDao.class); - _podDao = locator.getDao(HostPodDao.class); - _serviceOfferingDao = locator.getDao(ServiceOfferingDao.class); - _snapshotDao = locator.getDao(SnapshotDao.class); - _storagePoolDao = locator.getDao(StoragePoolDao.class); - _templateDao = locator.getDao(VMTemplateDao.class); - _templateDetailsDao = locator.getDao(VMTemplateDetailsDao.class); - _templateHostDao = locator.getDao(VMTemplateHostDao.class); - _templateSwiftDao = locator.getDao(VMTemplateSwiftDao.class); - _templateS3Dao = locator.getDao(VMTemplateS3Dao.class); - _uploadDao = locator.getDao(UploadDao.class); - _userDao = locator.getDao(UserDao.class); - _userStatsDao = locator.getDao(UserStatisticsDao.class); - _userVmDao = locator.getDao(UserVmDao.class); - _userVmJoinDao = locator.getDao(UserVmJoinDao.class); - _vlanDao = locator.getDao(VlanDao.class); - _volumeDao = locator.getDao(VolumeDao.class); - _site2SiteVpnGatewayDao = locator.getDao(Site2SiteVpnGatewayDao.class); - _site2SiteCustomerGatewayDao = locator.getDao(Site2SiteCustomerGatewayDao.class); - _volumeHostDao = locator.getDao(VolumeHostDao.class); - _zoneDao = locator.getDao(DataCenterDao.class); - _securityGroupDao = locator.getDao(SecurityGroupDao.class); - _securityGroupJoinDao = locator.getDao(SecurityGroupJoinDao.class); - _networkOfferingDao = locator.getDao(NetworkOfferingDao.class); - _networkDao = locator.getDao(NetworkDao.class); - _physicalNetworkDao = locator.getDao(PhysicalNetworkDao.class); - _configDao = locator.getDao(ConfigurationDao.class); - _consoleProxyDao = locator.getDao(ConsoleProxyDao.class); - _firewallCidrsDao = locator.getDao(FirewallRulesCidrsDao.class); - _vmDao = locator.getDao(VMInstanceDao.class); - _resourceLimitMgr = locator.getManager(ResourceLimitService.class); - _projectMgr = locator.getManager(ProjectService.class); - _resourceMgr = locator.getManager(ResourceManager.class); - _accountDetailsDao = locator.getDao(AccountDetailsDao.class); - _networkDomainDao = locator.getDao(NetworkDomainDao.class); - _haMgr = locator.getManager(HighAvailabilityManager.class); - _vpcMgr = locator.getManager(VpcManager.class); - _taggedResourceService = locator.getManager(TaggedResourceService.class); - _sshKeyPairDao = locator.getDao(SSHKeyPairDao.class); - _userVmDetailsDao = locator.getDao(UserVmDetailsDao.class); - _asConditionDao = locator.getDao(ConditionDao.class); - _asPolicyDao = locator.getDao(AutoScalePolicyDao.class); - _asPolicyConditionMapDao = locator.getDao(AutoScalePolicyConditionMapDao.class); - _counterDao = locator.getDao(CounterDao.class); - _asVmGroupPolicyMapDao = locator.getDao(AutoScaleVmGroupPolicyMapDao.class); - _tagJoinDao = locator.getDao(ResourceTagJoinDao.class); - _vmGroupJoinDao = locator.getDao(InstanceGroupJoinDao.class); - _eventJoinDao = locator.getDao(EventJoinDao.class); - _userAccountJoinDao = locator.getDao(UserAccountJoinDao.class); - _projectJoinDao = locator.getDao(ProjectJoinDao.class); - _projectAccountJoinDao = locator.getDao(ProjectAccountJoinDao.class); - _projectInvitationJoinDao = locator.getDao(ProjectInvitationJoinDao.class); - _hostJoinDao = locator.getDao(HostJoinDao.class); - _volJoinDao = locator.getDao(VolumeJoinDao.class); - _poolJoinDao = locator.getDao(StoragePoolJoinDao.class); - _accountJoinDao = locator.getDao(AccountJoinDao.class); - _jobJoinDao = locator.getDao(AsyncJobJoinDao.class); - - _physicalNetworkTrafficTypeDao = locator.getDao(PhysicalNetworkTrafficTypeDao.class); - _physicalNetworkServiceProviderDao = locator.getDao(PhysicalNetworkServiceProviderDao.class); - _firewallRuleDao = locator.getDao(FirewallRulesDao.class); - _staticRouteDao = locator.getDao(StaticRouteDao.class); - _vpcGatewayDao = locator.getDao(VpcGatewayDao.class); - _asVmProfileDao = locator.getDao(AutoScaleVmProfileDao.class); - _asVmGroupDao = locator.getDao(AutoScaleVmGroupDao.class); - _vpcDao = locator.getDao(VpcDao.class); - _vpcOfferingDao = locator.getDao(VpcOfferingDao.class); - _snapshotPolicyDao = locator.getDao(SnapshotPolicyDao.class); - _asyncJobDao = locator.getDao(AsyncJobDao.class); // Note: stats collector should already have been initialized by this time, otherwise a null instance is returned _statsCollector = StatsCollector.getInstance(); @@ -611,7 +521,7 @@ public class ApiDBUtils { } public static boolean isChildDomain(long parentId, long childId) { - return _domainDao.isChildDomain(parentId, childId); + return _domainDao.isChildDomain(parentId, childId); } public static DomainRouterVO findDomainRouterById(Long routerId) { @@ -1016,7 +926,7 @@ public class ApiDBUtils { else scaleDownPolicyIds.add(autoScalePolicy.getId()); } - } + } public static String getKeyPairName(String sshPublicKey) { SSHKeyPairVO sshKeyPair = _sshKeyPairDao.findByPublicKey(sshPublicKey); //key might be removed prior to this point @@ -1226,7 +1136,7 @@ public class ApiDBUtils { } public static DomainRouterResponse fillRouterDetails(DomainRouterResponse vrData, DomainRouterJoinVO vr){ - return _domainRouterJoinDao.setDomainRouterResponse(vrData, vr); + return _domainRouterJoinDao.setDomainRouterResponse(vrData, vr); } public static List newDomainRouterView(VirtualRouter vr){ @@ -1238,7 +1148,7 @@ public class ApiDBUtils { } public static UserVmResponse fillVmDetails(UserVmResponse vmData, UserVmJoinVO vm){ - return _userVmJoinDao.setUserVmResponse(vmData, vm); + return _userVmJoinDao.setUserVmResponse(vmData, vm); } public static List newUserVmView(UserVm... userVms){ @@ -1250,7 +1160,7 @@ public class ApiDBUtils { } public static SecurityGroupResponse fillSecurityGroupDetails(SecurityGroupResponse vsgData, SecurityGroupJoinVO sg){ - return _securityGroupJoinDao.setSecurityGroupResponse(vsgData, sg); + return _securityGroupJoinDao.setSecurityGroupResponse(vsgData, sg); } public static List newSecurityGroupView(SecurityGroup sg){ @@ -1312,7 +1222,7 @@ public class ApiDBUtils { } public static ProjectResponse fillProjectDetails(ProjectResponse rsp, ProjectJoinVO proj){ - return _projectJoinDao.setProjectResponse(rsp,proj); + return _projectJoinDao.setProjectResponse(rsp,proj); } public static List newProjectView(Project proj){ @@ -1344,7 +1254,7 @@ public class ApiDBUtils { } public static HostResponse fillHostDetails(HostResponse vrData, HostJoinVO vr){ - return _hostJoinDao.setHostResponse(vrData, vr); + return _hostJoinDao.setHostResponse(vrData, vr); } public static List newHostView(Host vr){ @@ -1358,42 +1268,42 @@ public class ApiDBUtils { public static VolumeResponse fillVolumeDetails(VolumeResponse vrData, VolumeJoinVO vr){ return _volJoinDao.setVolumeResponse(vrData, vr); - } + } - public static List newVolumeView(Volume vr){ - return _volJoinDao.newVolumeView(vr); - } + public static List newVolumeView(Volume vr){ + return _volJoinDao.newVolumeView(vr); + } - public static StoragePoolResponse newStoragePoolResponse(StoragePoolJoinVO vr) { - return _poolJoinDao.newStoragePoolResponse(vr); - } + public static StoragePoolResponse newStoragePoolResponse(StoragePoolJoinVO vr) { + return _poolJoinDao.newStoragePoolResponse(vr); + } - public static StoragePoolResponse fillStoragePoolDetails(StoragePoolResponse vrData, StoragePoolJoinVO vr){ + public static StoragePoolResponse fillStoragePoolDetails(StoragePoolResponse vrData, StoragePoolJoinVO vr){ return _poolJoinDao.setStoragePoolResponse(vrData, vr); - } + } - public static List newStoragePoolView(StoragePool vr){ - return _poolJoinDao.newStoragePoolView(vr); - } + public static List newStoragePoolView(StoragePool vr){ + return _poolJoinDao.newStoragePoolView(vr); + } - public static AccountResponse newAccountResponse(AccountJoinVO ve) { - return _accountJoinDao.newAccountResponse(ve); - } + public static AccountResponse newAccountResponse(AccountJoinVO ve) { + return _accountJoinDao.newAccountResponse(ve); + } - public static AccountJoinVO newAccountView(Account e){ - return _accountJoinDao.newAccountView(e); - } + public static AccountJoinVO newAccountView(Account e){ + return _accountJoinDao.newAccountView(e); + } - public static AccountJoinVO findAccountViewById(Long accountId) { - return _accountJoinDao.findByIdIncludingRemoved(accountId); - } + public static AccountJoinVO findAccountViewById(Long accountId) { + return _accountJoinDao.findByIdIncludingRemoved(accountId); + } - public static AsyncJobResponse newAsyncJobResponse(AsyncJobJoinVO ve) { - return _jobJoinDao.newAsyncJobResponse(ve); - } + public static AsyncJobResponse newAsyncJobResponse(AsyncJobJoinVO ve) { + return _jobJoinDao.newAsyncJobResponse(ve); + } - public static AsyncJobJoinVO newAsyncJobView(AsyncJob e){ - return _jobJoinDao.newAsyncJobView(e); - } + public static AsyncJobJoinVO newAsyncJobView(AsyncJob e){ + return _jobJoinDao.newAsyncJobView(e); + } } diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java index a0ef6e24358..9b1afb27394 100755 --- a/server/src/com/cloud/api/ApiDispatcher.java +++ b/server/src/com/cloud/api/ApiDispatcher.java @@ -24,76 +24,69 @@ import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.regex.Matcher; -import com.cloud.dao.EntityManager; -import com.cloud.utils.ReflectUtil; +import javax.inject.Inject; + import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.InfrastructureEntity; -import org.apache.cloudstack.acl.Role; -import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseCmd.CommandType; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.PlugService; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.Validate; +import org.apache.cloudstack.api.command.user.event.ListEventsCmd; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; -import org.apache.cloudstack.api.BaseCmd.CommandType; -import org.apache.cloudstack.api.command.user.event.ListEventsCmd; import com.cloud.async.AsyncCommandQueued; import com.cloud.async.AsyncJobManager; import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dao.EntityManager; import com.cloud.exception.AccountLimitException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.network.dao.NetworkDao; -import com.cloud.server.ManagementServer; -import com.cloud.storage.dao.VMTemplateDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.UserContext; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.component.Inject; -import com.cloud.utils.component.PluggableService; -import com.cloud.utils.db.GenericDao; +import com.cloud.utils.ReflectUtil; import com.cloud.utils.exception.CSExceptionErrorCode; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.uuididentity.dao.IdentityDao; -// ApiDispatcher: A class that dispatches API commands to the appropriate manager for execution. +@Component public class ApiDispatcher { private static final Logger s_logger = Logger.getLogger(ApiDispatcher.class.getName()); - ComponentLocator _locator; Long _createSnapshotQueueSizeLimit; - @Inject private AsyncJobManager _asyncMgr = null; - @Inject private AccountManager _accountMgr = null; + @Inject AsyncJobManager _asyncMgr = null; + @Inject AccountManager _accountMgr = null; @Inject EntityManager _entityMgr = null; @Inject IdentityDao _identityDao = null; + @Inject ConfigurationDao _configDao = null; - Map> _daoNameMap = new HashMap>(); - // singleton class - private static ApiDispatcher s_instance = ApiDispatcher.getInstance(); - - public static ApiDispatcher getInstance() { - if (s_instance == null) { - s_instance = ComponentLocator.inject(ApiDispatcher.class); - } - return s_instance; - } + private static ApiDispatcher s_instance; protected ApiDispatcher() { super(); - _locator = ComponentLocator.getLocator(ManagementServer.Name); - ConfigurationDao configDao = _locator.getDao(ConfigurationDao.class); - Map configs = configDao.getConfiguration(); + Map configs = _configDao.getConfiguration(); String strSnapshotLimit = configs.get(Config.ConcurrentSnapshotsThresholdPerHost.key()); if (strSnapshotLimit != null) { Long snapshotLimit = NumbersUtil.parseLong(strSnapshotLimit, 1L); @@ -104,12 +97,11 @@ public class ApiDispatcher { _createSnapshotQueueSizeLimit = snapshotLimit; } } - _daoNameMap.put("com.cloud.network.Network", NetworkDao.class); - _daoNameMap.put("com.cloud.template.VirtualMachineTemplate", VMTemplateDao.class); + s_instance = this; } public void dispatchCreateCmd(BaseAsyncCreateCmd cmd, Map params) { - processParameters(cmd, params); + processParameters(cmd, params); try { UserContext ctx = UserContext.current(); @@ -206,113 +198,113 @@ public class ApiDispatcher { } catch (Throwable t) { if (t instanceof InvalidParameterValueException) { - // earlier, we'd log the db id as part of the log message, but now since we've pushed - // the id into a IdentityProxy object, we would need to dump that object alongwith the - // message. - InvalidParameterValueException ref = (InvalidParameterValueException) t; - ServerApiException ex = new ServerApiException(BaseCmd.PARAM_ERROR, t.getMessage()); + // earlier, we'd log the db id as part of the log message, but now since we've pushed + // the id into a IdentityProxy object, we would need to dump that object alongwith the + // message. + InvalidParameterValueException ref = (InvalidParameterValueException) t; + ServerApiException ex = new ServerApiException(BaseCmd.PARAM_ERROR, t.getMessage()); // copy over the IdentityProxy information as well and throw the serverapiexception. ArrayList idList = ref.getIdProxyList(); if (idList != null) { - // Iterate through entire arraylist and copy over each proxy id. - for (int i = 0 ; i < idList.size(); i++) { - ex.addProxyObject(idList.get(i)); - s_logger.info(t.getMessage() + " uuid: " + idList.get(i)); - } + // Iterate through entire arraylist and copy over each proxy id. + for (int i = 0 ; i < idList.size(); i++) { + ex.addProxyObject(idList.get(i)); + s_logger.info(t.getMessage() + " uuid: " + idList.get(i)); + } } else { - s_logger.info(t.getMessage()); + s_logger.info(t.getMessage()); } // Also copy over the cserror code. - ex.setCSErrorCode(ref.getCSErrorCode()); + ex.setCSErrorCode(ref.getCSErrorCode()); throw ex; } else if(t instanceof IllegalArgumentException) { - throw new ServerApiException(BaseCmd.PARAM_ERROR, t.getMessage()); + throw new ServerApiException(BaseCmd.PARAM_ERROR, t.getMessage()); } else if (t instanceof PermissionDeniedException) { - PermissionDeniedException ref = (PermissionDeniedException)t; - ServerApiException ex = new ServerApiException(BaseCmd.ACCOUNT_ERROR, t.getMessage()); + PermissionDeniedException ref = (PermissionDeniedException)t; + ServerApiException ex = new ServerApiException(BaseCmd.ACCOUNT_ERROR, t.getMessage()); // copy over the IdentityProxy information as well and throw the serverapiexception. - ArrayList idList = ref.getIdProxyList(); + ArrayList idList = ref.getIdProxyList(); if (idList != null) { - // Iterate through entire arraylist and copy over each proxy id. - for (int i = 0 ; i < idList.size(); i++) { - ex.addProxyObject(idList.get(i)); - s_logger.info("PermissionDenied: " + t.getMessage() + "uuid: " + idList.get(i)); - } - } else { - s_logger.info("PermissionDenied: " + t.getMessage()); - } - // Also copy over the cserror code. - ex.setCSErrorCode(ref.getCSErrorCode()); - throw ex; - } else if (t instanceof AccountLimitException) { - AccountLimitException ref = (AccountLimitException)t; - ServerApiException ex = new ServerApiException(BaseCmd.ACCOUNT_RESOURCE_LIMIT_ERROR, t.getMessage()); - // copy over the IdentityProxy information as well and throw the serverapiexception. - ArrayList idList = ref.getIdProxyList(); - if (idList != null) { - // Iterate through entire arraylist and copy over each proxy id. - for (int i = 0 ; i < idList.size(); i++) { - ex.addProxyObject(idList.get(i)); - s_logger.info(t.getMessage() + "uuid: " + idList.get(i)); - } + // Iterate through entire arraylist and copy over each proxy id. + for (int i = 0 ; i < idList.size(); i++) { + ex.addProxyObject(idList.get(i)); + s_logger.info("PermissionDenied: " + t.getMessage() + "uuid: " + idList.get(i)); + } } else { - s_logger.info(t.getMessage()); + s_logger.info("PermissionDenied: " + t.getMessage()); } // Also copy over the cserror code. - ex.setCSErrorCode(ref.getCSErrorCode()); + ex.setCSErrorCode(ref.getCSErrorCode()); + throw ex; + } else if (t instanceof AccountLimitException) { + AccountLimitException ref = (AccountLimitException)t; + ServerApiException ex = new ServerApiException(BaseCmd.ACCOUNT_RESOURCE_LIMIT_ERROR, t.getMessage()); + // copy over the IdentityProxy information as well and throw the serverapiexception. + ArrayList idList = ref.getIdProxyList(); + if (idList != null) { + // Iterate through entire arraylist and copy over each proxy id. + for (int i = 0 ; i < idList.size(); i++) { + ex.addProxyObject(idList.get(i)); + s_logger.info(t.getMessage() + "uuid: " + idList.get(i)); + } + } else { + s_logger.info(t.getMessage()); + } + // Also copy over the cserror code. + ex.setCSErrorCode(ref.getCSErrorCode()); throw ex; } else if (t instanceof InsufficientCapacityException) { - InsufficientCapacityException ref = (InsufficientCapacityException)t; - ServerApiException ex = new ServerApiException(BaseCmd.INSUFFICIENT_CAPACITY_ERROR, t.getMessage()); + InsufficientCapacityException ref = (InsufficientCapacityException)t; + ServerApiException ex = new ServerApiException(BaseCmd.INSUFFICIENT_CAPACITY_ERROR, t.getMessage()); // copy over the IdentityProxy information as well and throw the serverapiexception. - ArrayList idList = ref.getIdProxyList(); + ArrayList idList = ref.getIdProxyList(); if (idList != null) { - // Iterate through entire arraylist and copy over each proxy id. - for (int i = 0 ; i < idList.size(); i++) { - ex.addProxyObject(idList.get(i)); - s_logger.info(t.getMessage() + "uuid: " + idList.get(i)); - } + // Iterate through entire arraylist and copy over each proxy id. + for (int i = 0 ; i < idList.size(); i++) { + ex.addProxyObject(idList.get(i)); + s_logger.info(t.getMessage() + "uuid: " + idList.get(i)); + } } else { - s_logger.info(t.getMessage()); + s_logger.info(t.getMessage()); } // Also copy over the cserror code - ex.setCSErrorCode(ref.getCSErrorCode()); + ex.setCSErrorCode(ref.getCSErrorCode()); throw ex; } else if (t instanceof ResourceAllocationException) { - ResourceAllocationException ref = (ResourceAllocationException)t; + ResourceAllocationException ref = (ResourceAllocationException)t; ServerApiException ex = new ServerApiException(BaseCmd.RESOURCE_ALLOCATION_ERROR, t.getMessage()); // copy over the IdentityProxy information as well and throw the serverapiexception. ArrayList idList = ref.getIdProxyList(); if (idList != null) { - // Iterate through entire arraylist and copy over each proxy id. - for (int i = 0 ; i < idList.size(); i++) { - String id = idList.get(i); - ex.addProxyObject(id); - s_logger.warn("Exception: " + t.getMessage() + "uuid: " + id); - } + // Iterate through entire arraylist and copy over each proxy id. + for (int i = 0 ; i < idList.size(); i++) { + String id = idList.get(i); + ex.addProxyObject(id); + s_logger.warn("Exception: " + t.getMessage() + "uuid: " + id); + } } else { - s_logger.warn("Exception: ", t); + s_logger.warn("Exception: ", t); } // Also copy over the cserror code. - ex.setCSErrorCode(ref.getCSErrorCode()); + ex.setCSErrorCode(ref.getCSErrorCode()); throw ex; } else if (t instanceof ResourceUnavailableException) { - ResourceUnavailableException ref = (ResourceUnavailableException)t; + ResourceUnavailableException ref = (ResourceUnavailableException)t; ServerApiException ex = new ServerApiException(BaseCmd.RESOURCE_UNAVAILABLE_ERROR, t.getMessage()); // copy over the IdentityProxy information as well and throw the serverapiexception. ArrayList idList = ref.getIdProxyList(); if (idList != null) { - // Iterate through entire arraylist and copy over each proxy id. - for (int i = 0 ; i < idList.size(); i++) { - String id = idList.get(i); - ex.addProxyObject(id); - s_logger.warn("Exception: " + t.getMessage() + "uuid: " + id); - } + // Iterate through entire arraylist and copy over each proxy id. + for (int i = 0 ; i < idList.size(); i++) { + String id = idList.get(i); + ex.addProxyObject(id); + s_logger.warn("Exception: " + t.getMessage() + "uuid: " + id); + } } else { - s_logger.warn("Exception: ", t); + s_logger.warn("Exception: ", t); } // Also copy over the cserror code. - ex.setCSErrorCode(ref.getCSErrorCode()); + ex.setCSErrorCode(ref.getCSErrorCode()); throw ex; } else if (t instanceof AsyncCommandQueued) { throw (AsyncCommandQueued) t; @@ -323,18 +315,18 @@ public class ApiDispatcher { s_logger.error("Exception while executing " + cmd.getClass().getSimpleName() + ":", t); ServerApiException ex; if (UserContext.current().getCaller().getType() == Account.ACCOUNT_TYPE_ADMIN) { - ex = new ServerApiException(BaseCmd.INTERNAL_ERROR, t.getMessage()); + ex = new ServerApiException(BaseCmd.INTERNAL_ERROR, t.getMessage()); } else { ex = new ServerApiException(BaseCmd.INTERNAL_ERROR, BaseCmd.USER_ERROR_MESSAGE); } ex.setCSErrorCode(CSExceptionErrorCode.getCSErrCode(ex.getClass().getName())); - throw ex; + throw ex; } } } @SuppressWarnings({ "unchecked", "rawtypes" }) - public static void processParameters(BaseCmd cmd, Map params) { + public static void processParameters(BaseCmd cmd, Map params) { List entitiesToAccess = new ArrayList(); Map unpackedParams = cmd.unpackParams(params); @@ -348,7 +340,7 @@ public class ApiDispatcher { if ((unpackedParams.get(ApiConstants.PAGE) == null) && (pageSize != null && pageSize != BaseListCmd.PAGESIZE_UNLIMITED)) { ServerApiException ex = new ServerApiException(BaseCmd.PARAM_ERROR, "\"page\" parameter is required when \"pagesize\" is specified"); ex.setCSErrorCode(CSExceptionErrorCode.getCSErrCode(ex.getClass().getName())); - throw ex; + throw ex; } else if (pageSize == null && (unpackedParams.get(ApiConstants.PAGE) != null)) { throw new ServerApiException(BaseCmd.PARAM_ERROR, "\"pagesize\" parameter is required when \"page\" is specified"); } @@ -399,14 +391,14 @@ public class ApiDispatcher { throw new ServerApiException(BaseCmd.PARAM_ERROR, "Unable to execute API command " + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + " due to invalid value. " + invEx.getMessage()); } catch (CloudRuntimeException cloudEx) { // FIXME: Better error message? This only happens if the API command is not executable, which typically - //means + //means // there was // and IllegalAccessException setting one of the parameters. throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Internal error executing API command " + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8)); } //check access on the resource this field points to - try { + try { ACL checkAccess = field.getAnnotation(ACL.class); CommandType fieldType = parameterAnnotation.type(); @@ -426,17 +418,17 @@ public class ApiDispatcher { // Check if the parameter type is a single // Id or list of id's/name's switch (fieldType) { - case LIST: - CommandType listType = parameterAnnotation.collectionType(); - switch (listType) { - case LONG: - case UUID: - List listParam = (List) field.get(cmd); - for (Long entityId : listParam) { - Object entityObj = s_instance._entityMgr.findById(entity, entityId); - entitiesToAccess.add(entityObj); - } - break; + case LIST: + CommandType listType = parameterAnnotation.collectionType(); + switch (listType) { + case LONG: + case UUID: + List listParam = (List) field.get(cmd); + for (Long entityId : listParam) { + Object entityObj = s_instance._entityMgr.findById(entity, entityId); + entitiesToAccess.add(entityObj); + } + break; /* * case STRING: List listParam = * new ArrayList(); listParam = @@ -448,17 +440,17 @@ public class ApiDispatcher { * entitiesToAccess.add(entityObj); } * break; */ - default: - break; - } - break; - case LONG: - case UUID: - Object entityObj = s_instance._entityMgr.findById(entity, (Long) field.get(cmd)); - entitiesToAccess.add(entityObj); - break; default: break; + } + break; + case LONG: + case UUID: + Object entityObj = s_instance._entityMgr.findById(entity, (Long) field.get(cmd)); + entitiesToAccess.add(entityObj); + break; + default: + break; } if (ControlledEntity.class.isAssignableFrom(entity)) { @@ -474,17 +466,17 @@ public class ApiDispatcher { } } - } + } - } + } - } catch (IllegalArgumentException e) { - s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible."); - throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]"); - } catch (IllegalAccessException e) { - s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible."); - throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]"); - } + } catch (IllegalArgumentException e) { + s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible."); + throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]"); + } catch (IllegalAccessException e) { + s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible."); + throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]"); + } } @@ -533,7 +525,7 @@ public class ApiDispatcher { // Invoke the getId method, get the internal long ID // If that fails hide exceptions as the uuid may not exist try { - internalId = (Long) ((InternalIdentity)objVO).getId(); + internalId = ((InternalIdentity)objVO).getId(); } catch (IllegalArgumentException e) { } catch (NullPointerException e) { } @@ -598,7 +590,7 @@ public class ApiDispatcher { // we ignore blank or null values and defer to the command to set a default // value for optional parameters ... if (paramObj != null && isNotBlank(paramObj.toString())) { - field.set(cmdObj, Float.valueOf(paramObj.toString())); + field.set(cmdObj, Float.valueOf(paramObj.toString())); } break; case INTEGER: @@ -606,7 +598,7 @@ public class ApiDispatcher { // we ignore blank or null values and defer to the command to set a default // value for optional parameters ... if (paramObj != null && isNotBlank(paramObj.toString())) { - field.set(cmdObj, Integer.valueOf(paramObj.toString())); + field.set(cmdObj, Integer.valueOf(paramObj.toString())); } break; case LIST: @@ -619,16 +611,16 @@ public class ApiDispatcher { case INTEGER: listParam.add(Integer.valueOf(token)); break; - case UUID: - if (token.isEmpty()) - break; - Long internalId = translateUuidToInternalId(token, annotation); - listParam.add(internalId); - break; - case LONG: { - listParam.add(Long.valueOf(token)); - } + case UUID: + if (token.isEmpty()) + break; + Long internalId = translateUuidToInternalId(token, annotation); + listParam.add(internalId); break; + case LONG: { + listParam.add(Long.valueOf(token)); + } + break; case SHORT: listParam.add(Short.valueOf(token)); case STRING: @@ -645,7 +637,7 @@ public class ApiDispatcher { field.set(cmdObj, internalId); break; case LONG: - field.set(cmdObj, Long.valueOf(paramObj.toString())); + field.set(cmdObj, Long.valueOf(paramObj.toString())); break; case SHORT: field.set(cmdObj, Short.valueOf(paramObj.toString())); @@ -686,30 +678,26 @@ public class ApiDispatcher { } public static void plugService(Field field, BaseCmd cmd) { - ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); - Class fc = field.getType(); - Object instance = null; - if (PluggableService.class.isAssignableFrom(fc)) { - instance = locator.getPluggableService(fc); - } + Class fc = field.getType(); + Object instance = null; - if (instance == null) { + if (instance == null) { throw new CloudRuntimeException("Unable to plug service " + fc.getSimpleName() + " in command " + cmd.getClass().getSimpleName()); - } + } + + try { + field.setAccessible(true); + field.set(cmd, instance); + } catch (IllegalArgumentException e) { + s_logger.error("IllegalArgumentException at plugService for command " + cmd.getCommandName() + ", field " + field.getName()); + throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [Illegal argumet at field " + field.getName() + "]"); + } catch (IllegalAccessException e) { + s_logger.error("Error at plugService for command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible."); + throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]"); + } + } - try { - field.setAccessible(true); - field.set(cmd, instance); - } catch (IllegalArgumentException e) { - s_logger.error("IllegalArgumentException at plugService for command " + cmd.getCommandName() + ", field " + field.getName()); - throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [Illegal argumet at field " + field.getName() + "]"); - } catch (IllegalAccessException e) { - s_logger.error("Error at plugService for command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible."); - throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]"); - } - } - public static Long getIdentiyId(String tableName, String token) { return s_instance._identityDao.getIdentityId(tableName, token); } diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index b61f10a1ade..4e2355e9058 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import javax.ejb.Local; +import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; @@ -97,15 +98,13 @@ import com.cloud.ha.HighAvailabilityManager; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.security.SecurityGroupVMMapVO; import com.cloud.network.security.dao.SecurityGroupVMMapDao; -import com.cloud.projects.ProjectInvitation; -import com.cloud.projects.Project.ListProjectResourcesCriteria; import com.cloud.projects.Project; +import com.cloud.projects.Project.ListProjectResourcesCriteria; +import com.cloud.projects.ProjectInvitation; import com.cloud.projects.ProjectManager; import com.cloud.projects.dao.ProjectAccountDao; import com.cloud.projects.dao.ProjectDao; import com.cloud.server.Criteria; -import com.cloud.storage.StoragePool; -import com.cloud.storage.StoragePoolVO; import com.cloud.storage.Volume; import com.cloud.user.Account; import com.cloud.user.AccountManager; @@ -115,7 +114,6 @@ import com.cloud.user.dao.AccountDao; import com.cloud.utils.DateUtil; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; -import com.cloud.utils.component.Inject; import com.cloud.utils.component.Manager; import com.cloud.utils.db.Filter; import com.cloud.utils.db.SearchBuilder; @@ -137,7 +135,7 @@ public class QueryManagerImpl implements QueryService, Manager { private String _name; - // public static ViewResponseHelper _responseGenerator; + // public static ViewResponseHelper _responseGenerator; @Inject private AccountManager _accountMgr; @@ -214,7 +212,7 @@ public class QueryManagerImpl implements QueryService, Manager { @Override public boolean configure(String name, Map params) throws ConfigurationException { _name = name; - // _responseGenerator = new ViewResponseHelper(); + // _responseGenerator = new ViewResponseHelper(); return false; } @@ -717,7 +715,7 @@ public class QueryManagerImpl implements QueryService, Manager { if (tags != null && !tags.isEmpty()) { int count = 0; - for (String key : tags.keySet()) { + for (String key : tags.keySet()) { sc.setParameters("key" + String.valueOf(count), key); sc.setParameters("value" + String.valueOf(count), tags.get(key)); count++; @@ -883,10 +881,10 @@ public class QueryManagerImpl implements QueryService, Manager { if (tags != null && !tags.isEmpty()) { int count = 0; for (String key : tags.keySet()) { - sc.setParameters("key" + String.valueOf(count), key); - sc.setParameters("value" + String.valueOf(count), tags.get(key)); - count++; - } + sc.setParameters("key" + String.valueOf(count), key); + sc.setParameters("value" + String.valueOf(count), tags.get(key)); + count++; + } } if (securityGroup != null) { @@ -974,10 +972,10 @@ public class QueryManagerImpl implements QueryService, Manager { //Filter searchFilter = new Filter(DomainRouterJoinVO.class, null, true, cmd.getStartIndex(), cmd.getPageSizeVal()); SearchBuilder sb = _routerJoinDao.createSearchBuilder(); sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct - // ids to get - // number of - // records with - // pagination + // ids to get + // number of + // records with + // pagination _accountMgr.buildACLViewSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); sb.and("name", sb.entity().getHostName(), SearchCriteria.Op.LIKE); @@ -1095,7 +1093,7 @@ public class QueryManagerImpl implements QueryService, Manager { Filter searchFilter = new Filter(ProjectJoinVO.class, "id", false, startIndex, pageSize); SearchBuilder sb = _projectJoinDao.createSearchBuilder(); sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct - // ids + // ids if (_accountMgr.isAdmin(caller.getType())) { if (domainId != null) { @@ -1302,7 +1300,7 @@ public class QueryManagerImpl implements QueryService, Manager { Long startIndex = cmd.getStartIndex(); Long pageSizeVal = cmd.getPageSizeVal(); - //long projectId, String accountName, String role, Long startIndex, Long pageSizeVal) { + //long projectId, String accountName, String role, Long startIndex, Long pageSizeVal) { Account caller = UserContext.current().getCaller(); //check that the project exists @@ -1546,7 +1544,7 @@ public class QueryManagerImpl implements QueryService, Manager { if (tags != null && !tags.isEmpty()) { int count = 0; - for (String key : tags.keySet()) { + for (String key : tags.keySet()) { sc.setParameters("key" + String.valueOf(count), key); sc.setParameters("value" + String.valueOf(count), tags.get(key)); count++; diff --git a/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java index 1ce9b33c009..54c276dccbd 100755 --- a/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java +++ b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java @@ -81,7 +81,7 @@ import com.cloud.user.UserContext; import com.cloud.uservm.UserVm; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; -import com.cloud.utils.component.Adapters; +import com.cloud.utils.component.AdapterBase; import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; @@ -181,7 +181,7 @@ public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMet * prepare() will check if current account has right for creating * template */ - TemplateAdapter adapter = Adapters.getAdapterByName(_adapters, TemplateAdapterType.BareMetal.getName()); + TemplateAdapter adapter = AdapterBase.getAdapterByName(_adapters, TemplateAdapterType.BareMetal.getName()); Long userId = UserContext.current().getCallerUserId(); userId = (userId == null ? User.UID_SYSTEM : userId); AccountVO account = _accountDao.findById(vm.getAccountId()); diff --git a/server/src/com/cloud/baremetal/PxeServerManagerImpl.java b/server/src/com/cloud/baremetal/PxeServerManagerImpl.java index 6e123afc710..7a9a783969f 100755 --- a/server/src/com/cloud/baremetal/PxeServerManagerImpl.java +++ b/server/src/com/cloud/baremetal/PxeServerManagerImpl.java @@ -41,7 +41,7 @@ import com.cloud.resource.ResourceStateAdapter; import com.cloud.resource.ServerResource; import com.cloud.resource.UnableDeleteHostException; import com.cloud.uservm.UserVm; -import com.cloud.utils.component.Adapters; +import com.cloud.utils.component.AdapterBase; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.ReservationContext; import com.cloud.vm.UserVmVO; @@ -87,7 +87,7 @@ public class PxeServerManagerImpl implements PxeServerManager, ResourceStateAdap protected PxeServerService getServiceByType(String type) { PxeServerService _service; - _service = Adapters.getAdapterByName(_services, type); + _service = AdapterBase.getAdapterByName(_services, type); if (_service == null) { throw new CloudRuntimeException("Cannot find PXE service for " + type); } diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index bb943c96600..a8c1743be63 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -39,30 +39,32 @@ import javax.naming.NamingException; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; +import org.apache.cloudstack.acl.SecurityChecker; +import org.apache.cloudstack.api.ApiConstants.LDAPParams; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd; import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd; -import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; +import org.apache.cloudstack.api.command.admin.network.DeleteNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.network.UpdateNetworkOfferingCmd; import org.apache.cloudstack.api.command.admin.offering.CreateDiskOfferingCmd; -import org.apache.cloudstack.api.command.admin.offering.*; +import org.apache.cloudstack.api.command.admin.offering.CreateServiceOfferingCmd; +import org.apache.cloudstack.api.command.admin.offering.DeleteDiskOfferingCmd; +import org.apache.cloudstack.api.command.admin.offering.DeleteServiceOfferingCmd; +import org.apache.cloudstack.api.command.admin.offering.UpdateDiskOfferingCmd; +import org.apache.cloudstack.api.command.admin.offering.UpdateServiceOfferingCmd; import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd; import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd; import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; -import org.apache.cloudstack.api.command.admin.offering.CreateServiceOfferingCmd; -import org.apache.cloudstack.api.command.admin.offering.DeleteServiceOfferingCmd; -import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; import org.apache.cloudstack.api.command.user.network.ListNetworkOfferingsCmd; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; -import org.apache.cloudstack.acl.SecurityChecker; import com.cloud.alert.AlertManager; -import org.apache.cloudstack.api.ApiConstants.LDAPParams; import com.cloud.api.ApiDBUtils; import com.cloud.capacity.dao.CapacityDao; import com.cloud.configuration.Resource.ResourceType; @@ -153,8 +155,6 @@ import com.cloud.user.UserContext; import com.cloud.user.dao.AccountDao; import com.cloud.utils.NumbersUtil; import com.cloud.utils.StringUtils; -import com.cloud.utils.component.Adapters; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; @@ -216,7 +216,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura // @com.cloud.utils.component.Inject(adapter = SecurityChecker.class) @Inject List _secChecker; - + @Inject CapacityDao _capacityDao; @Inject @@ -438,7 +438,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura String name = cmd.getCfgName(); String value = cmd.getValue(); UserContext.current().setEventDetails(" Name: " + name + " New Value: " + (((name.toLowerCase()).contains("password")) ? "*****" : - (((value == null) ? "" : value)))); + (((value == null) ? "" : value)))); // check if config value exists ConfigurationVO config = _configDao.findByName(name); if (config == null) { @@ -1454,9 +1454,9 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura } if (internalDns2 == null) { - internalDns2 = zone.getInternalDns2(); + internalDns2 = zone.getInternalDns2(); } - + if (guestCidr == null) { guestCidr = zone.getGuestNetworkCidr(); } @@ -1915,8 +1915,8 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura String description = cmd.getDisplayText(); Long numGibibytes = cmd.getDiskSize(); boolean isCustomized = cmd.isCustomized() != null ? cmd.isCustomized() : false; // false - // by - // default + // by + // default String tags = cmd.getTags(); // Long domainId = cmd.getDomainId() != null ? cmd.getDomainId() : // Long.valueOf(DomainVO.ROOT_DOMAIN); // disk offering @@ -2106,7 +2106,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura physicalNetworkId = network.getPhysicalNetworkId(); } } - + // Verify that zone exists DataCenterVO zone = _zoneDao.findById(zoneId); if (zone == null) { @@ -2148,8 +2148,8 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura } } } - - + + // Check if zone is enabled Account caller = UserContext.current().getCaller(); if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { @@ -2234,7 +2234,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura //check resource limits _resourceLimitMgr.checkResourceLimit(vlanOwner, ResourceType.public_ip, accountIpRange); - + associateIpRangeToAccount = true; } } @@ -2272,24 +2272,24 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura public Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, Long podId, String startIP, String endIP, String vlanGateway, String vlanNetmask, String vlanId, Account vlanOwner) { - - + + Network network = _networkMgr.getNetwork(networkId); - + //Validate the zone DataCenterVO zone = _zoneDao.findById(zoneId); if (zone == null) { throw new InvalidParameterValueException("Please specify a valid zone."); } - + // ACL check checkZoneAccess(UserContext.current().getCaller(), zone); - + //Validate the physical network if (_physicalNetworkDao.findById(physicalNetworkId) == null) { throw new InvalidParameterValueException("Please specify a valid physical network id"); } - + //Validate the pod if (podId != null) { Pod pod = _podDao.findById(podId); @@ -2302,10 +2302,10 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura //pod vlans can be created in basic zone only if (zone.getNetworkType() != NetworkType.Basic || network.getTrafficType() != TrafficType.Guest) { throw new InvalidParameterValueException("Pod id can be specified only for the networks of type " - + TrafficType.Guest + " in zone of type " + NetworkType.Basic); + + TrafficType.Guest + " in zone of type " + NetworkType.Basic); } } - + //1) if vlan is specified for the guest network range, it should be the same as network's vlan //2) if vlan is missing, default it to the guest network's vlan if (network.getTrafficType() == TrafficType.Guest) { @@ -2315,7 +2315,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura String[] vlan = uri.toString().split("vlan:\\/\\/"); networkVlanId = vlan[1]; } - + if (vlanId != null) { // if vlan is specified, throw an error if it's not equal to network's vlanId if (networkVlanId != null && !networkVlanId.equalsIgnoreCase(vlanId)) { @@ -2328,14 +2328,14 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura //vlan id is required for public network throw new InvalidParameterValueException("Vlan id is required when add ip range to the public network"); } - + if (vlanId == null) { vlanId = Vlan.UNTAGGED; } VlanType vlanType = forVirtualNetwork ? VlanType.VirtualNetwork : VlanType.DirectAttached; - - + + if (vlanOwner != null && zone.getNetworkType() != NetworkType.Advanced) { throw new InvalidParameterValueException("Vlan owner can be defined only in the zone of type " + NetworkType.Advanced); } @@ -2484,7 +2484,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura if (vlan == null) { throw new InvalidParameterValueException("Please specify a valid IP range id."); } - + boolean isAccountSpecific = false; List acctVln = _accountVlanMapDao.listAccountVlanMapsByVlan(vlan.getId()); // Check for account wide pool. It will have an entry for account_vlan_map. @@ -2502,25 +2502,25 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura if (vlan == null) { throw new CloudRuntimeException("Unable to acquire vlan configuration: " + vlanDbId); } - + if (s_logger.isDebugEnabled()) { s_logger.debug("lock vlan " + vlanDbId + " is acquired"); } - + List ips = _publicIpAddressDao.listByVlanId(vlanDbId); - + for (IPAddressVO ip : ips) { if (ip.isOneToOneNat()) { throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + " as ip " + ip + " belonging to the range is used for static nat purposes. Cleanup the rules first"); } - + if (ip.isSourceNat() && _networkMgr.getNetwork(ip.getAssociatedWithNetworkId()) != null) { throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + " as ip " + ip + " belonging to the range is a source nat ip for the network id=" + ip.getSourceNetworkId() + ". IP range with the source nat ip address can be removed either as a part of Network, or account removal"); } - + if (_firewallDao.countRulesByIpId(ip.getId()) > 0) { throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + " as ip " + ip + " belonging to the range has firewall rules applied. Cleanup the rules first"); @@ -2613,7 +2613,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura return true; } - + @DB protected boolean savePublicIPRange(String startIP, String endIP, long zoneId, long vlanDbId, long sourceNetworkid, long physicalNetworkId) { @@ -2816,7 +2816,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura } } - + private boolean validPod(long podId) { return (_podDao.findById(podId) != null); } @@ -3021,7 +3021,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura // in Acton, don't allow to specify more than 1 provider per service if (svcPrv.get(serviceStr) != null && svcPrv.get(serviceStr).size() > 1) { throw new InvalidParameterValueException("In the current release only one provider can be " + - "specified for the service"); + "specified for the service"); } for (String prvNameStr : svcPrv.get(serviceStr)) { // check if provider is supported @@ -3033,7 +3033,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura if (provider == Provider.JuniperSRX) { firewallProvider = Provider.JuniperSRX; } - + if ((service == Service.PortForwarding || service == Service.StaticNat) && provider == Provider.VirtualRouter){ firewallProvider = Provider.VirtualRouter; } @@ -3053,7 +3053,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura serviceProviderMap.put(service, providers); } else { throw new InvalidParameterValueException("Service " + serviceStr + " is not enabled for the network " + - "offering, can't add a provider to it"); + "offering, can't add a provider to it"); } } } @@ -3202,7 +3202,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura String multicastRateStr = _configDao.getValue("multicast.throttling.rate"); int multicastRate = ((multicastRateStr == null) ? 10 : Integer.parseInt(multicastRateStr)); tags = cleanupTags(tags); - + if (specifyVlan != specifyIpRanges) { throw new InvalidParameterValueException("SpecifyVlan should be equal to specifyIpRanges which is " + specifyIpRanges); } @@ -3211,11 +3211,11 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura if (!specifyVlan && type == GuestType.Shared) { throw new InvalidParameterValueException("SpecifyVlan should be true if network offering's type is " + type); } - + //specifyIpRanges should always be false for Isolated offering with Source nat service enabled if (specifyVlan && type == GuestType.Isolated && serviceProviderMap.containsKey(Service.SourceNat)) { throw new InvalidParameterValueException("SpecifyVlan should be false if the network offering type is " - + type + " and service " + Service.SourceNat.getName() + " is supported"); + + type + " and service " + Service.SourceNat.getName() + " is supported"); } // validate availability value @@ -3235,7 +3235,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura } } - + boolean dedicatedLb = false; boolean elasticLb = false; boolean sharedSourceNat = false; @@ -3245,7 +3245,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura boolean inline = false; if (serviceCapabilityMap != null && !serviceCapabilityMap.isEmpty()) { Map lbServiceCapabilityMap = serviceCapabilityMap.get(Service.Lb); - + if ((lbServiceCapabilityMap != null) && (!lbServiceCapabilityMap.isEmpty())) { String isolationCapability = lbServiceCapabilityMap.get(Capability.SupportedLBIsolation); if (isolationCapability != null) { @@ -3259,7 +3259,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura if (param != null) { elasticLb = param.contains("true"); } - + String inlineMode = lbServiceCapabilityMap.get(Capability.InlineMode); if (inlineMode != null) { _networkMgr.checkCapabilityForProvider(serviceProviderMap.get(Service.Lb), Service.Lb, Capability.InlineMode, inlineMode); @@ -3326,7 +3326,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura _ntwkOffServiceMapDao.persist(offService); s_logger.trace("Added service for the network offering: " + offService + " with provider " + provider.getName()); } - + if (vpcOff) { List supportedSvcs = new ArrayList(); supportedSvcs.addAll(serviceProviderMap.keySet()); @@ -3547,7 +3547,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura if (sourceNatSupported != null) { addOffering = addOffering && (_networkMgr.areServicesSupportedByNetworkOffering(offering.getId(), Network.Service.SourceNat) == sourceNatSupported); } - + if (forVpc != null) { addOffering = addOffering && (isOfferingForVpc(offering) == forVpc.booleanValue()); } else if (network != null){ @@ -3666,14 +3666,14 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura } if (availability == null) { throw new InvalidParameterValueException("Invalid value for Availability. Supported types: " - + Availability.Required + ", " + Availability.Optional); + + Availability.Required + ", " + Availability.Optional); } else { if (availability == NetworkOffering.Availability.Required) { boolean canOffBeRequired = (offeringToUpdate.getGuestType() == GuestType.Isolated && _networkMgr.areServicesSupportedByNetworkOffering(offeringToUpdate.getId(), Service.SourceNat)); if (!canOffBeRequired) { throw new InvalidParameterValueException("Availability can be " + - NetworkOffering.Availability.Required + " only for networkOfferings of type " + GuestType.Isolated + " and with " + NetworkOffering.Availability.Required + " only for networkOfferings of type " + GuestType.Isolated + " and with " + Service.SourceNat.getName() + " enabled"); } @@ -3681,7 +3681,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura List offerings = _networkOfferingDao.listByAvailability(Availability.Required, false); if (!offerings.isEmpty() && offerings.get(0).getId() != offeringToUpdate.getId()) { throw new InvalidParameterValueException("System already has network offering id=" + - offerings.get(0).getId() + " with availability " + Availability.Required); + offerings.get(0).getId() + " with availability " + Availability.Required); } } offering.setAvailability(availability); @@ -3697,12 +3697,12 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura @Override @ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_MARK_DEFAULT_ZONE, eventDescription = "Marking account with the " + - "default zone", async=true) + "default zone", async=true) public AccountVO markDefaultZone(String accountName, long domainId, long defaultZoneId) { - - // Check if the account exists - Account account = _accountDao.findEnabledAccount(accountName, domainId); - if (account == null) { + + // Check if the account exists + Account account = _accountDao.findEnabledAccount(accountName, domainId); + if (account == null) { s_logger.error("Unable to find account by name: " + accountName + " in domain " + domainId); throw new InvalidParameterValueException("Account by name: " + accountName + " doesn't exist in domain " + domainId); } @@ -3710,20 +3710,20 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura // Don't allow modification of system account if (account.getId() == Account.ACCOUNT_ID_SYSTEM) { throw new InvalidParameterValueException("Can not modify system account"); - } + } - AccountVO acctForUpdate = _accountDao.findById(account.getId()); - - acctForUpdate.setDefaultZoneId(defaultZoneId); - - if (_accountDao.update(account.getId(), acctForUpdate)) { - UserContext.current().setEventDetails("Default zone id= " + defaultZoneId); - return _accountDao.findById(account.getId()); - } else { - return null; - } + AccountVO acctForUpdate = _accountDao.findById(account.getId()); + + acctForUpdate.setDefaultZoneId(defaultZoneId); + + if (_accountDao.update(account.getId(), acctForUpdate)) { + UserContext.current().setEventDetails("Default zone id= " + defaultZoneId); + return _accountDao.findById(account.getId()); + } else { + return null; + } } - + // Note: This method will be used for entity name validations in the coming // releases (place holder for now) private void validateEntityName(String str) { @@ -3851,31 +3851,31 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura public ClusterVO getCluster(long id) { return _clusterDao.findById(id); } - + @Override public AllocationState findClusterAllocationState(ClusterVO cluster){ - - if(cluster.getAllocationState() == AllocationState.Disabled){ - return AllocationState.Disabled; - }else if(ApiDBUtils.findPodById(cluster.getPodId()).getAllocationState() == AllocationState.Disabled){ - return AllocationState.Disabled; - }else { - DataCenterVO zone = ApiDBUtils.findZoneById(cluster.getDataCenterId()); - return zone.getAllocationState(); - } + + if(cluster.getAllocationState() == AllocationState.Disabled){ + return AllocationState.Disabled; + }else if(ApiDBUtils.findPodById(cluster.getPodId()).getAllocationState() == AllocationState.Disabled){ + return AllocationState.Disabled; + }else { + DataCenterVO zone = ApiDBUtils.findZoneById(cluster.getDataCenterId()); + return zone.getAllocationState(); + } } @Override public AllocationState findPodAllocationState(HostPodVO pod){ - - if(pod.getAllocationState() == AllocationState.Disabled){ - return AllocationState.Disabled; - }else { - DataCenterVO zone = ApiDBUtils.findZoneById(pod.getDataCenterId()); - return zone.getAllocationState(); - } + + if(pod.getAllocationState() == AllocationState.Disabled){ + return AllocationState.Disabled; + }else { + DataCenterVO zone = ApiDBUtils.findZoneById(pod.getDataCenterId()); + return zone.getAllocationState(); + } } - + private boolean allowIpRangeOverlap(VlanVO vlan, boolean forVirtualNetwork, long networkId) { // FIXME - delete restriction for virtual network in the future if (vlan.getVlanType() == VlanType.DirectAttached && !forVirtualNetwork) { diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 5b8109ff6db..b6f69d1bdf4 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -64,12 +64,7 @@ import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.*; import com.cloud.dc.DataCenter.NetworkType; -import com.cloud.dc.DataCenterVO; -import com.cloud.dc.Pod; -import com.cloud.dc.PodVlanMapVO; -import com.cloud.dc.Vlan; import com.cloud.dc.Vlan.VlanType; -import com.cloud.dc.VlanVO; import com.cloud.dc.dao.AccountVlanMapDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.PodVlanMapDao; @@ -112,13 +107,6 @@ import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy; import com.cloud.network.lb.LoadBalancingRulesManager; import com.cloud.network.rules.*; import com.cloud.network.rules.FirewallRule.Purpose; -import com.cloud.network.rules.FirewallRuleVO; -import com.cloud.network.rules.PortForwardingRule; -import com.cloud.network.rules.PortForwardingRuleVO; -import com.cloud.network.rules.RulesManager; -import com.cloud.network.rules.StaticNat; -import com.cloud.network.rules.StaticNatRule; -import com.cloud.network.rules.StaticNatRuleImpl; import com.cloud.network.rules.dao.PortForwardingRulesDao; import com.cloud.network.vpc.NetworkACLManager; import com.cloud.network.vpc.PrivateIpVO; @@ -143,7 +131,7 @@ import com.cloud.user.dao.UserStatisticsDao; import com.cloud.utils.AnnotationHelper; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; -import com.cloud.utils.component.Adapters; +import com.cloud.utils.component.AdapterBase; import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.JoinBuilder.JoinType; @@ -306,7 +294,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag @Override public NetworkElement getElementImplementingProvider(String providerName) { String elementName = s_providerToNetworkElementMap.get(providerName); - NetworkElement element = Adapters.getAdapterByName(_networkElements, elementName); + NetworkElement element = AdapterBase.getAdapterByName(_networkElements, elementName); return element; } @@ -1813,7 +1801,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag NetworkVO ntwkVO = _networksDao.findById(network.getId()); s_logger.debug("Allocating nic for vm " + vm.getVirtualMachine() + " in network " + network + " with requested profile " + requested); - NetworkGuru guru = Adapters.getAdapterByName(_networkGurus, ntwkVO.getGuruName()); + NetworkGuru guru = AdapterBase.getAdapterByName(_networkGurus, ntwkVO.getGuruName()); if (requested != null && requested.getMode() == null) { requested.setMode(network.getMode()); @@ -1958,7 +1946,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } try { - NetworkGuru guru = Adapters.getAdapterByName(_networkGurus, network.getGuruName()); + NetworkGuru guru = AdapterBase.getAdapterByName(_networkGurus, network.getGuruName()); Network.State state = network.getState(); if (state == Network.State.Implemented || state == Network.State.Implementing) { s_logger.debug("Network id=" + networkId + " is already implemented"); @@ -2165,7 +2153,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { Integer networkRate = getNetworkRate(network.getId(), vmProfile.getId()); - NetworkGuru guru = Adapters.getAdapterByName(_networkGurus, network.getGuruName()); + NetworkGuru guru = AdapterBase.getAdapterByName(_networkGurus, network.getGuruName()); NicVO nic = _nicDao.findById(nicId); NicProfile profile = null; @@ -2227,7 +2215,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag NetworkVO network = _networksDao.findById(nic.getNetworkId()); Integer networkRate = getNetworkRate(network.getId(), vm.getId()); - NetworkGuru guru = Adapters.getAdapterByName(_networkGurus, network.getGuruName()); + NetworkGuru guru = AdapterBase.getAdapterByName(_networkGurus, network.getGuruName()); NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, isSecurityGroupSupportedInNetwork(network), getNetworkTag(vm.getHypervisorType(), network)); guru.updateNicProfile(profile, network); @@ -2271,7 +2259,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag if (originalState == Nic.State.Reserved || originalState == Nic.State.Reserving) { if (nic.getReservationStrategy() == Nic.ReservationStrategy.Start) { - NetworkGuru guru = Adapters.getAdapterByName(_networkGurus, network.getGuruName()); + NetworkGuru guru = AdapterBase.getAdapterByName(_networkGurus, network.getGuruName()); nic.setState(Nic.State.Releasing); _nicDao.update(nic.getId(), nic); NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), null, @@ -2321,7 +2309,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag NetworkVO network = _networksDao.findById(nic.getNetworkId()); Integer networkRate = getNetworkRate(network.getId(), vm.getId()); - NetworkGuru guru = Adapters.getAdapterByName(_networkGurus, network.getGuruName()); + NetworkGuru guru = AdapterBase.getAdapterByName(_networkGurus, network.getGuruName()); NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, isSecurityGroupSupportedInNetwork(network), getNetworkTag(vm.getHypervisorType(), network)); guru.updateNicProfile(profile, network); @@ -2342,7 +2330,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag NetworkVO network = _networksDao.findById(networkId); Integer networkRate = getNetworkRate(network.getId(), vm.getId()); - NetworkGuru guru = Adapters.getAdapterByName(_networkGurus, network.getGuruName()); + NetworkGuru guru = AdapterBase.getAdapterByName(_networkGurus, network.getGuruName()); NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, isSecurityGroupSupportedInNetwork(network), getNetworkTag(vm.getHypervisorType(), network)); guru.updateNicProfile(profile, network); @@ -2497,7 +2485,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag NetworkVO network = _networksDao.findById(nic.getNetworkId()); NicProfile profile = new NicProfile(nic, network, null, null, null, isSecurityGroupSupportedInNetwork(network), getNetworkTag(vm.getHypervisorType(), network)); - NetworkGuru guru = Adapters.getAdapterByName(_networkGurus, network.getGuruName()); + NetworkGuru guru = AdapterBase.getAdapterByName(_networkGurus, network.getGuruName()); guru.deallocate(network, profile, vm); _nicDao.remove(nic.getId()); s_logger.debug("Removed nic id=" + nic.getId()); @@ -3560,7 +3548,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag if (s_logger.isDebugEnabled()) { s_logger.debug("Network id=" + networkId + " is shutdown successfully, cleaning up corresponding resources now."); } - NetworkGuru guru = Adapters.getAdapterByName(_networkGurus, network.getGuruName()); + NetworkGuru guru = AdapterBase.getAdapterByName(_networkGurus, network.getGuruName()); NetworkProfile profile = convertNetworkToNetworkProfile(network.getId()); guru.shutdown(profile, _networkOfferingDao.findById(network.getNetworkOfferingId())); @@ -3726,7 +3714,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag if (s_logger.isDebugEnabled()) { s_logger.debug("Network id=" + networkId + " is destroyed successfully, cleaning up corresponding resources now."); } - NetworkGuru guru = Adapters.getAdapterByName(_networkGurus, network.getGuruName()); + NetworkGuru guru = AdapterBase.getAdapterByName(_networkGurus, network.getGuruName()); Account owner = _accountMgr.getAccount(network.getAccountId()); Transaction txn = Transaction.currentTxn(); @@ -4429,7 +4417,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag @Override public NetworkProfile convertNetworkToNetworkProfile(long networkId) { NetworkVO network = _networksDao.findById(networkId); - NetworkGuru guru = Adapters.getAdapterByName(_networkGurus, network.getGuruName()); + NetworkGuru guru = AdapterBase.getAdapterByName(_networkGurus, network.getGuruName()); NetworkProfile profile = new NetworkProfile(network); guru.updateNetworkProfile(profile); diff --git a/server/src/com/cloud/servlet/CloudStartupServlet.java b/server/src/com/cloud/servlet/CloudStartupServlet.java index de133abb16e..4b662910083 100755 --- a/server/src/com/cloud/servlet/CloudStartupServlet.java +++ b/server/src/com/cloud/servlet/CloudStartupServlet.java @@ -45,11 +45,11 @@ public class CloudStartupServlet extends HttpServlet implements ServletContextLi initLog4j(); // Save Configuration Values - ConfigurationServer c = ComponentContext.getCompanent(ConfigurationServer.class); + ConfigurationServer c = ComponentContext.getComponent(ConfigurationServer.class); try { c.persistDefaultValues(); - ManagementServer ms = ComponentContext.getCompanent(ManagementServer.class); + ManagementServer ms = ComponentContext.getComponent(ManagementServer.class); ms.startup(); ms.enableAdminUser("password"); ApiServer.initApiServer(ms.getPropertiesFiles()); diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index 0e914725e16..0c208530b54 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -130,7 +130,7 @@ import com.cloud.user.dao.UserAccountDao; import com.cloud.user.dao.UserDao; import com.cloud.uservm.UserVm; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.component.Adapters; +import com.cloud.utils.component.AdapterBase; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; @@ -216,10 +216,10 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe private TemplateAdapter getAdapter(HypervisorType type) { TemplateAdapter adapter = null; if (type == HypervisorType.BareMetal) { - adapter = Adapters.getAdapterByName(_adapters, TemplateAdapterType.BareMetal.getName()); + adapter = AdapterBase.getAdapterByName(_adapters, TemplateAdapterType.BareMetal.getName()); } else { // see HyervisorTemplateAdapter - adapter = Adapters.getAdapterByName(_adapters, TemplateAdapterType.Hypervisor.getName()); + adapter = AdapterBase.getAdapterByName(_adapters, TemplateAdapterType.Hypervisor.getName()); } if (adapter == null) { diff --git a/server/test/com/cloud/async/TestAsyncJobManager.java b/server/test/com/cloud/async/TestAsyncJobManager.java index 8ce51fa3849..d45ca4dc54a 100644 --- a/server/test/com/cloud/async/TestAsyncJobManager.java +++ b/server/test/com/cloud/async/TestAsyncJobManager.java @@ -20,7 +20,10 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import javax.inject.Inject; + import junit.framework.Assert; +import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -31,102 +34,99 @@ import com.cloud.exception.PermissionDeniedException; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.host.dao.HostDaoImpl; -import com.cloud.server.ManagementServer; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.Transaction; -import com.cloud.utils.testcase.ComponentSetup; -import com.cloud.utils.testcase.ComponentTestCase; -@ComponentSetup(managerName="management-server", setupXml="async-job-component.xml") -public class TestAsyncJobManager extends ComponentTestCase { +public class TestAsyncJobManager extends TestCase { public static final Logger s_logger = Logger.getLogger(TestAsyncJobManager.class.getName()); - + volatile long s_count = 0; - public void asyncCall() { - AsyncJobManager asyncMgr = ComponentLocator.getLocator(ManagementServer.Name).getManager(AsyncJobManager.class); + @Inject AsyncJobManager asyncMgr; + public void asyncCall() { // long jobId = mgr.rebootVirtualMachineAsync(1, 1); long jobId = 0L; - s_logger.info("Async-call job id: " + jobId); - - while(true) { - AsyncJobResult result; - try { - result = asyncMgr.queryAsyncJobResult(jobId); - - if(result.getJobStatus() != AsyncJobResult.STATUS_IN_PROGRESS) { - s_logger.info("Async-call completed, result: " + result.toString()); - break; - } - s_logger.info("Async-call is in progress, progress: " + result.toString()); - - } catch (PermissionDeniedException e1) { - } - - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - } - } - } - - public void sequence() { - final HostDao hostDao = new HostDaoImpl(); - long seq = hostDao.getNextSequence(1); - s_logger.info("******* seq : " + seq + " ********"); - - HashMap hashMap = new HashMap(); - final Map map = Collections.synchronizedMap(hashMap); - - s_count = 0; - final long maxCount = 1000000; // test one million times - - Thread t1 = new Thread(new Runnable() { - public void run() { - while(s_count < maxCount) { - s_count++; - long seq = hostDao.getNextSequence(1); - Assert.assertTrue(map.put(seq, seq) == null); - } - } - }); - - Thread t2 = new Thread(new Runnable() { - public void run() { - while(s_count < maxCount) { - s_count++; - long seq = hostDao.getNextSequence(1); - Assert.assertTrue(map.put(seq, seq) == null); - } - } - }); - - t1.start(); - t2.start(); - - try { - t1.join(); - t2.join(); - } catch (InterruptedException e) { - } - } + s_logger.info("Async-call job id: " + jobId); - /* + while(true) { + AsyncJobResult result; + try { + result = asyncMgr.queryAsyncJobResult(jobId); + + if(result.getJobStatus() != AsyncJobResult.STATUS_IN_PROGRESS) { + s_logger.info("Async-call completed, result: " + result.toString()); + break; + } + s_logger.info("Async-call is in progress, progress: " + result.toString()); + + } catch (PermissionDeniedException e1) { + } + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + } + } + + public void sequence() { + final HostDao hostDao = new HostDaoImpl(); + long seq = hostDao.getNextSequence(1); + s_logger.info("******* seq : " + seq + " ********"); + + HashMap hashMap = new HashMap(); + final Map map = Collections.synchronizedMap(hashMap); + + s_count = 0; + final long maxCount = 1000000; // test one million times + + Thread t1 = new Thread(new Runnable() { + @Override + public void run() { + while(s_count < maxCount) { + s_count++; + long seq = hostDao.getNextSequence(1); + Assert.assertTrue(map.put(seq, seq) == null); + } + } + }); + + Thread t2 = new Thread(new Runnable() { + @Override + public void run() { + while(s_count < maxCount) { + s_count++; + long seq = hostDao.getNextSequence(1); + Assert.assertTrue(map.put(seq, seq) == null); + } + } + }); + + t1.start(); + t2.start(); + + try { + t1.join(); + t2.join(); + } catch (InterruptedException e) { + } + } + + /* public void ipAssignment() { final IPAddressDao ipAddressDao = new IPAddressDaoImpl(); - + final ConcurrentHashMap map = new ConcurrentHashMap(); //final Map map = Collections.synchronizedMap(hashMap); - + s_count = 0; final long maxCount = 1000000; // test one million times - + Thread t1 = new Thread(new Runnable() { public void run() { while(s_count < maxCount) { s_count++; - + Transaction txn = Transaction.open("Alex1"); try { IPAddressVO addr = ipAddressDao.assignIpAddress(1, 0, 1, false); @@ -141,12 +141,12 @@ public class TestAsyncJobManager extends ComponentTestCase { } } }); - + Thread t2 = new Thread(new Runnable() { public void run() { while(s_count < maxCount) { s_count++; - + Transaction txn = Transaction.open("Alex2"); try { IPAddressVO addr = ipAddressDao.assignIpAddress(1, 0, 1, false); @@ -157,96 +157,97 @@ public class TestAsyncJobManager extends ComponentTestCase { } } }); - + t1.start(); t2.start(); - + try { t1.join(); t2.join(); } catch (InterruptedException e) { } } - */ - - private long getRandomLockId() { - return 1L; - - /* - * will use in the future test cases + */ + + private long getRandomLockId() { + return 1L; + + /* + * will use in the future test cases int i = new Random().nextInt(); if(i % 2 == 0) return 1L; return 2L; - */ - } - - public void tstLocking() { - - int testThreads = 20; - Thread[] threads = new Thread[testThreads]; - - for(int i = 0; i < testThreads; i++) { - final int current = i; - threads[i] = new Thread(new Runnable() { - public void run() { - - final HostDao hostDao = new HostDaoImpl(); - while(true) { - Transaction txn = Transaction.currentTxn(); - try { - HostVO host = hostDao.acquireInLockTable(getRandomLockId(), 10); - if(host != null) { - s_logger.info("Thread " + (current + 1) + " acquired lock"); - - try { Thread.sleep(getRandomMilliseconds(1000, 5000)); } catch (InterruptedException e) {} - - s_logger.info("Thread " + (current + 1) + " released lock"); - hostDao.releaseFromLockTable(host.getId()); - - try { Thread.sleep(getRandomMilliseconds(1000, 5000)); } catch (InterruptedException e) {} - } else { - s_logger.info("Thread " + (current + 1) + " is not able to acquire lock"); - } - } finally { - txn.close(); - } - } - } - }); - threads[i].start(); - } - - try { - for(int i = 0; i < testThreads; i++) - threads[i].join(); - } catch(InterruptedException e) { - } - } - - public void testDomain() { - getRandomMilliseconds(1, 100); - DomainDao domainDao = new DomainDaoImpl(); - - DomainVO domain1 = new DomainVO("d1", 2L, 1L, null); - domainDao.create(domain1); - - DomainVO domain2 = new DomainVO("d2", 2L, 1L, null); - domainDao.create(domain2); - - DomainVO domain3 = new DomainVO("d3", 2L, 1L, null); - domainDao.create(domain3); + */ + } - DomainVO domain11 = new DomainVO("d11", 2L, domain1.getId(), null); - domainDao.create(domain11); - - domainDao.remove(domain11.getId()); - - DomainVO domain12 = new DomainVO("d12", 2L, domain1.getId(), null); - domainDao.create(domain12); - - domainDao.remove(domain3.getId()); - DomainVO domain4 = new DomainVO("d4", 2L, 1L, null); - domainDao.create(domain4); - } + public void tstLocking() { + + int testThreads = 20; + Thread[] threads = new Thread[testThreads]; + + for(int i = 0; i < testThreads; i++) { + final int current = i; + threads[i] = new Thread(new Runnable() { + @Override + public void run() { + + final HostDao hostDao = new HostDaoImpl(); + while(true) { + Transaction txn = Transaction.currentTxn(); + try { + HostVO host = hostDao.acquireInLockTable(getRandomLockId(), 10); + if(host != null) { + s_logger.info("Thread " + (current + 1) + " acquired lock"); + + try { Thread.sleep(getRandomMilliseconds(1000, 5000)); } catch (InterruptedException e) {} + + s_logger.info("Thread " + (current + 1) + " released lock"); + hostDao.releaseFromLockTable(host.getId()); + + try { Thread.sleep(getRandomMilliseconds(1000, 5000)); } catch (InterruptedException e) {} + } else { + s_logger.info("Thread " + (current + 1) + " is not able to acquire lock"); + } + } finally { + txn.close(); + } + } + } + }); + threads[i].start(); + } + + try { + for(int i = 0; i < testThreads; i++) + threads[i].join(); + } catch(InterruptedException e) { + } + } + + public void testDomain() { + getRandomMilliseconds(1, 100); + DomainDao domainDao = new DomainDaoImpl(); + + DomainVO domain1 = new DomainVO("d1", 2L, 1L, null); + domainDao.create(domain1); + + DomainVO domain2 = new DomainVO("d2", 2L, 1L, null); + domainDao.create(domain2); + + DomainVO domain3 = new DomainVO("d3", 2L, 1L, null); + domainDao.create(domain3); + + DomainVO domain11 = new DomainVO("d11", 2L, domain1.getId(), null); + domainDao.create(domain11); + + domainDao.remove(domain11.getId()); + + DomainVO domain12 = new DomainVO("d12", 2L, domain1.getId(), null); + domainDao.create(domain12); + + domainDao.remove(domain3.getId()); + DomainVO domain4 = new DomainVO("d4", 2L, 1L, null); + domainDao.create(domain4); + } } diff --git a/server/test/com/cloud/async/TestSyncQueueManager.java b/server/test/com/cloud/async/TestSyncQueueManager.java index 2bbf7bcc8bd..32b7ddeb7d8 100644 --- a/server/test/com/cloud/async/TestSyncQueueManager.java +++ b/server/test/com/cloud/async/TestSyncQueueManager.java @@ -18,194 +18,187 @@ package com.cloud.async; import java.util.List; +import javax.inject.Inject; + +import junit.framework.TestCase; + import org.apache.log4j.Logger; import org.junit.Assert; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.testcase.ComponentSetup; -import com.cloud.utils.testcase.ComponentTestCase; -@ComponentSetup(managerName="management-server", setupXml="sync-queue-component.xml") -public class TestSyncQueueManager extends ComponentTestCase { +public class TestSyncQueueManager extends TestCase { public static final Logger s_logger = Logger.getLogger(TestSyncQueueManager.class.getName()); - + private volatile int count = 0; private volatile long expectingCurrent = 1; + @Inject SyncQueueManager mgr; - public void leftOverItems() { - SyncQueueManager mgr = ComponentLocator.getCurrentLocator().getManager( - SyncQueueManager.class); + public void leftOverItems() { - List l = mgr.getActiveQueueItems(1L, false); - if(l != null && l.size() > 0) { - for(SyncQueueItemVO item : l) { - s_logger.info("Left over item: " + item.toString()); - mgr.purgeItem(item.getId()); - } - } - } + List l = mgr.getActiveQueueItems(1L, false); + if(l != null && l.size() > 0) { + for(SyncQueueItemVO item : l) { + s_logger.info("Left over item: " + item.toString()); + mgr.purgeItem(item.getId()); + } + } + } - public void dequeueFromOneQueue() { - final SyncQueueManager mgr = ComponentLocator.getCurrentLocator().getManager( - SyncQueueManager.class); - - final int totalRuns = 5000; - final SyncQueueVO queue = mgr.queue("vm_instance", 1L, "Async-job", 1, 1); - for(int i = 1; i < totalRuns; i++) - mgr.queue("vm_instance", 1L, "Async-job", i+1, 1); - - count = 0; - expectingCurrent = 1; - Thread thread1 = new Thread(new Runnable() { - public void run() { - while(count < totalRuns) { - SyncQueueItemVO item = mgr.dequeueFromOne(queue.getId(), 1L); - if(item != null) { - s_logger.info("Thread 1 process item: " + item.toString()); - - Assert.assertEquals(expectingCurrent, item.getContentId().longValue()); - expectingCurrent++; - count++; - - mgr.purgeItem(item.getId()); - } - try { - Thread.sleep(getRandomMilliseconds(1, 10)); - } catch (InterruptedException e) { - } - } - } - } - ); - - Thread thread2 = new Thread(new Runnable() { - public void run() { - while(count < totalRuns) { - SyncQueueItemVO item = mgr.dequeueFromOne(queue.getId(), 1L); - if(item != null) { - s_logger.info("Thread 2 process item: " + item.toString()); - - Assert.assertEquals(expectingCurrent, item.getContentId().longValue()); - expectingCurrent++; - count++; - mgr.purgeItem(item.getId()); - } - - try { - Thread.sleep(getRandomMilliseconds(1, 10)); - } catch (InterruptedException e) { - } - } - } - } - ); - - thread1.start(); - thread2.start(); - try { - thread1.join(); - } catch (InterruptedException e) { - } - try { - thread2.join(); - } catch (InterruptedException e) { - } - - Assert.assertEquals(totalRuns, count); - } - - public void dequeueFromAnyQueue() { - final SyncQueueManager mgr = ComponentLocator.getCurrentLocator().getManager( - SyncQueueManager.class); + public void dequeueFromOneQueue() { + final int totalRuns = 5000; + final SyncQueueVO queue = mgr.queue("vm_instance", 1L, "Async-job", 1, 1); + for(int i = 1; i < totalRuns; i++) + mgr.queue("vm_instance", 1L, "Async-job", i+1, 1); - // simulate 30 queues - final int queues = 30; - final int totalRuns = 100; - final int itemsPerRun = 20; - for(int q = 1; q <= queues; q++) - for(int i = 0; i < totalRuns; i++) - mgr.queue("vm_instance", q, "Async-job", i+1, 1); - - count = 0; - Thread thread1 = new Thread(new Runnable() { - public void run() { - while(count < totalRuns*queues) { - List l = mgr.dequeueFromAny(1L, itemsPerRun); - if(l != null && l.size() > 0) { - s_logger.info("Thread 1 get " + l.size() + " dequeued items"); - - for(SyncQueueItemVO item : l) { - s_logger.info("Thread 1 process item: " + item.toString()); - count++; - - mgr.purgeItem(item.getId()); - } - } - try { - Thread.sleep(getRandomMilliseconds(1, 10)); - } catch (InterruptedException e) { - } - } - } - } - ); - - Thread thread2 = new Thread(new Runnable() { - public void run() { - while(count < totalRuns*queues) { - List l = mgr.dequeueFromAny(1L, itemsPerRun); - if(l != null && l.size() > 0) { - s_logger.info("Thread 2 get " + l.size() + " dequeued items"); - - for(SyncQueueItemVO item : l) { - s_logger.info("Thread 2 process item: " + item.toString()); - count++; - mgr.purgeItem(item.getId()); - } - } - - try { - Thread.sleep(getRandomMilliseconds(1, 10)); - } catch (InterruptedException e) { - } - } - } - } - ); - - thread1.start(); - thread2.start(); - try { - thread1.join(); - } catch (InterruptedException e) { - } - try { - thread2.join(); - } catch (InterruptedException e) { - } - Assert.assertEquals(queues*totalRuns, count); - } - - public void testPopulateQueueData() { - final int queues = 30000; - final int totalRuns = 100; - - final SyncQueueManager mgr = ComponentLocator.getCurrentLocator().getManager( - SyncQueueManager.class); - for(int q = 1; q <= queues; q++) - for(int i = 0; i < totalRuns; i++) - mgr.queue("vm_instance", q, "Async-job", i+1, 1); - } - + count = 0; + expectingCurrent = 1; + Thread thread1 = new Thread(new Runnable() { + @Override + public void run() { + while(count < totalRuns) { + SyncQueueItemVO item = mgr.dequeueFromOne(queue.getId(), 1L); + if(item != null) { + s_logger.info("Thread 1 process item: " + item.toString()); + + Assert.assertEquals(expectingCurrent, item.getContentId().longValue()); + expectingCurrent++; + count++; + + mgr.purgeItem(item.getId()); + } + try { + Thread.sleep(getRandomMilliseconds(1, 10)); + } catch (InterruptedException e) { + } + } + } + } + ); + + Thread thread2 = new Thread(new Runnable() { + @Override + public void run() { + while(count < totalRuns) { + SyncQueueItemVO item = mgr.dequeueFromOne(queue.getId(), 1L); + if(item != null) { + s_logger.info("Thread 2 process item: " + item.toString()); + + Assert.assertEquals(expectingCurrent, item.getContentId().longValue()); + expectingCurrent++; + count++; + mgr.purgeItem(item.getId()); + } + + try { + Thread.sleep(getRandomMilliseconds(1, 10)); + } catch (InterruptedException e) { + } + } + } + } + ); + + thread1.start(); + thread2.start(); + try { + thread1.join(); + } catch (InterruptedException e) { + } + try { + thread2.join(); + } catch (InterruptedException e) { + } + + Assert.assertEquals(totalRuns, count); + } + + public void dequeueFromAnyQueue() { + // simulate 30 queues + final int queues = 30; + final int totalRuns = 100; + final int itemsPerRun = 20; + for(int q = 1; q <= queues; q++) + for(int i = 0; i < totalRuns; i++) + mgr.queue("vm_instance", q, "Async-job", i+1, 1); + + count = 0; + Thread thread1 = new Thread(new Runnable() { + @Override + public void run() { + while(count < totalRuns*queues) { + List l = mgr.dequeueFromAny(1L, itemsPerRun); + if(l != null && l.size() > 0) { + s_logger.info("Thread 1 get " + l.size() + " dequeued items"); + + for(SyncQueueItemVO item : l) { + s_logger.info("Thread 1 process item: " + item.toString()); + count++; + + mgr.purgeItem(item.getId()); + } + } + try { + Thread.sleep(getRandomMilliseconds(1, 10)); + } catch (InterruptedException e) { + } + } + } + } + ); + + Thread thread2 = new Thread(new Runnable() { + @Override + public void run() { + while(count < totalRuns*queues) { + List l = mgr.dequeueFromAny(1L, itemsPerRun); + if(l != null && l.size() > 0) { + s_logger.info("Thread 2 get " + l.size() + " dequeued items"); + + for(SyncQueueItemVO item : l) { + s_logger.info("Thread 2 process item: " + item.toString()); + count++; + mgr.purgeItem(item.getId()); + } + } + + try { + Thread.sleep(getRandomMilliseconds(1, 10)); + } catch (InterruptedException e) { + } + } + } + } + ); + + thread1.start(); + thread2.start(); + try { + thread1.join(); + } catch (InterruptedException e) { + } + try { + thread2.join(); + } catch (InterruptedException e) { + } + Assert.assertEquals(queues*totalRuns, count); + } + + public void testPopulateQueueData() { + final int queues = 30000; + final int totalRuns = 100; + + for(int q = 1; q <= queues; q++) + for(int i = 0; i < totalRuns; i++) + mgr.queue("vm_instance", q, "Async-job", i+1, 1); + } + public void testSyncQueue() { - final SyncQueueManager mgr = ComponentLocator.getCurrentLocator().getManager( - SyncQueueManager.class); mgr.queue("vm_instance", 1, "Async-job", 1, 1); mgr.queue("vm_instance", 1, "Async-job", 2, 1); mgr.queue("vm_instance", 1, "Async-job", 3, 1); mgr.dequeueFromAny(100L, 1); - + List l = mgr.getBlockedQueueItems(100000, false); for(SyncQueueItemVO item : l) { System.out.println("Blocked item. " + item.getContentType() + "-" + item.getContentId()); diff --git a/server/test/com/cloud/cluster/CheckPointManagerTest.java b/server/test/com/cloud/cluster/CheckPointManagerTest.java deleted file mode 100755 index 74b069882b3..00000000000 --- a/server/test/com/cloud/cluster/CheckPointManagerTest.java +++ /dev/null @@ -1,390 +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. -package com.cloud.cluster; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import javax.ejb.Local; -import javax.naming.ConfigurationException; - -import junit.framework.TestCase; - -import org.apache.log4j.Logger; -import org.junit.After; -import org.junit.Before; - -import com.cloud.agent.Listener; -import com.cloud.agent.api.Answer; -import com.cloud.agent.api.Command; -import com.cloud.cluster.dao.StackMaidDao; -import com.cloud.cluster.dao.StackMaidDaoImpl; -import com.cloud.configuration.Config; -import com.cloud.configuration.DefaultInterceptorLibrary; -import com.cloud.configuration.dao.ConfigurationDaoImpl; -import com.cloud.exception.AgentUnavailableException; -import com.cloud.exception.OperationTimedoutException; -import com.cloud.host.Status.Event; -import com.cloud.serializer.SerializerHelper; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.component.MockComponentLocator; -import com.cloud.utils.db.Transaction; -import com.cloud.utils.exception.CloudRuntimeException; - -public class CheckPointManagerTest extends TestCase { - private final static Logger s_logger = Logger.getLogger(CheckPointManagerTest.class); - - @Override - @Before - public void setUp() { - MockComponentLocator locator = new MockComponentLocator("management-server"); - locator.addDao("StackMaidDao", StackMaidDaoImpl.class); - locator.addDao("ConfigurationDao", ConfigurationDaoImpl.class); - locator.addManager("ClusterManager", MockClusterManager.class); - locator.makeActive(new DefaultInterceptorLibrary()); - MockMaid.map.clear(); - s_logger.info("Cleaning up the database"); - Connection conn = Transaction.getStandaloneConnection(); - try { - conn.setAutoCommit(true); - PreparedStatement stmt = conn.prepareStatement("DELETE FROM stack_maid"); - stmt.executeUpdate(); - stmt.close(); - conn.close(); - } catch (SQLException e) { - throw new CloudRuntimeException("Unable to setup database", e); - } - } - - @Override - @After - public void tearDown() throws Exception { - } - - public void testCompleteCase() throws Exception { - ComponentLocator locator = ComponentLocator.getCurrentLocator(); - - CheckPointManagerImpl taskMgr = ComponentLocator.inject(CheckPointManagerImpl.class); - assertTrue(taskMgr.configure("TaskManager", new HashMap())); - assertTrue(taskMgr.start()); - - MockMaid delegate = new MockMaid(); - delegate.setValue("first"); - long taskId = taskMgr.pushCheckPoint(delegate); - - StackMaidDao maidDao = locator.getDao(StackMaidDao.class); - CheckPointVO task = maidDao.findById(taskId); - - assertEquals(task.getDelegate(), MockMaid.class.getName()); - MockMaid retrieved = (MockMaid)SerializerHelper.fromSerializedString(task.getContext()); - assertEquals(retrieved.getValue(), delegate.getValue()); - - delegate.setValue("second"); - taskMgr.updateCheckPointState(taskId, delegate); - - task = maidDao.findById(taskId); - assertEquals(task.getDelegate(), MockMaid.class.getName()); - retrieved = (MockMaid)SerializerHelper.fromSerializedString(task.getContext()); - assertEquals(retrieved.getValue(), delegate.getValue()); - - taskMgr.popCheckPoint(taskId); - assertNull(maidDao.findById(taskId)); - } - - public void testSimulatedReboot() throws Exception { - ComponentLocator locator = ComponentLocator.getCurrentLocator(); - - CheckPointManagerImpl taskMgr = ComponentLocator.inject(CheckPointManagerImpl.class); - assertTrue(taskMgr.configure("TaskManager", new HashMap())); - assertTrue(taskMgr.start()); - - MockMaid maid = new MockMaid(); - maid.setValue("first"); - long taskId = taskMgr.pushCheckPoint(maid); - - StackMaidDao maidDao = locator.getDao(StackMaidDao.class); - CheckPointVO task = maidDao.findById(taskId); - - assertEquals(task.getDelegate(), MockMaid.class.getName()); - MockMaid retrieved = (MockMaid)SerializerHelper.fromSerializedString(task.getContext()); - assertEquals(retrieved.getValue(), maid.getValue()); - - taskMgr.stop(); - - assertNotNull(MockMaid.map.get(maid.getSeq())); - - taskMgr = ComponentLocator.inject(CheckPointManagerImpl.class); - HashMap params = new HashMap(); - params.put(Config.TaskCleanupRetryInterval.key(), "1"); - taskMgr.configure("TaskManager", params); - taskMgr.start(); - - int i = 0; - while (MockMaid.map.get(maid.getSeq()) != null && i++ < 5) { - Thread.sleep(1000); - } - - assertNull(MockMaid.map.get(maid.getSeq())); - } - - public void testTakeover() throws Exception { - ComponentLocator locator = ComponentLocator.getCurrentLocator(); - - CheckPointManagerImpl taskMgr = ComponentLocator.inject(CheckPointManagerImpl.class); - assertTrue(taskMgr.configure("TaskManager", new HashMap())); - assertTrue(taskMgr.start()); - - MockMaid delegate = new MockMaid(); - delegate.setValue("first"); - long taskId = taskMgr.pushCheckPoint(delegate); - - StackMaidDao maidDao = locator.getDao(StackMaidDao.class); - CheckPointVO task = maidDao.findById(taskId); - - assertEquals(task.getDelegate(), MockMaid.class.getName()); - MockMaid retrieved = (MockMaid)SerializerHelper.fromSerializedString(task.getContext()); - assertEquals(retrieved.getValue(), delegate.getValue()); - - Connection conn = Transaction.getStandaloneConnection(); - try { - conn.setAutoCommit(true); - PreparedStatement stmt = conn.prepareStatement("update stack_maid set msid=? where msid=?"); - stmt.setLong(1, 1234); - stmt.setLong(2, ManagementServerNode.getManagementServerId()); - stmt.executeUpdate(); - stmt.close(); - } finally { - conn.close(); - } - - MockClusterManager clusterMgr = (MockClusterManager)locator.getManager(ClusterManager.class); - clusterMgr.triggerTakeover(1234); - - int i = 0; - while (MockMaid.map.get(delegate.getSeq()) != null && i++ < 500) { - Thread.sleep(1000); - } - - assertNull(MockMaid.map.get(delegate.getSeq())); - } - - public static class MockMaid implements CleanupMaid { - private static int s_seq = 1; - public static Map map = new ConcurrentHashMap(); - - int seq; - boolean canBeCleanup; - String value; - - protected MockMaid() { - canBeCleanup = true; - seq = s_seq++; - map.put(seq, this); - } - - public int getSeq() { - return seq; - } - - public String getValue() { - return value; - } - - public void setCanBeCleanup(boolean canBeCleanup) { - this.canBeCleanup = canBeCleanup; - } - - @Override - public int cleanup(CheckPointManager checkPointMgr) { - s_logger.debug("Cleanup called for " + seq); - map.remove(seq); - return canBeCleanup ? 0 : -1; - } - - public void setValue(String value) { - this.value = value; - } - - @Override - public String getCleanupProcedure() { - return "No cleanup necessary"; - } - } - - @Local(value=ClusterManager.class) - public static class MockClusterManager implements ClusterManager { - String _name; - ClusterManagerListener _listener; - - @Override - public boolean configure(String name, Map params) throws ConfigurationException { - _name = name; - return true; - } - - @Override - public boolean start() { - return true; - } - - @Override - public boolean stop() { - return true; - } - - @Override - public String getName() { - return _name; - } - - @Override - public void OnReceiveClusterServicePdu(ClusterServicePdu pdu) { - throw new CloudRuntimeException("Not implemented"); - } - - @Override - public Answer[] execute(String strPeer, long agentId, Command[] cmds, boolean stopOnError) { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public Answer[] sendToAgent(Long hostId, Command[] cmds, boolean stopOnError) throws AgentUnavailableException, OperationTimedoutException { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public boolean executeAgentUserRequest(long agentId, Event event) throws AgentUnavailableException { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public Boolean propagateAgentEvent(long agentId, Event event) throws AgentUnavailableException { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public int getHeartbeatThreshold() { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public long getManagementNodeId() { - return ManagementServerNode.getManagementServerId(); - } - - @Override - public boolean isManagementNodeAlive(long msid) { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public boolean pingManagementNode(long msid) { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public long getCurrentRunId() { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public String getSelfPeerName() { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public String getSelfNodeIP() { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public String getPeerName(long agentHostId) { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public void registerListener(ClusterManagerListener listener) { - _listener = listener; - } - - @Override - public void unregisterListener(ClusterManagerListener listener) { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public ManagementServerHostVO getPeer(String peerName) { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public void broadcast(long agentId, Command[] cmds) { - throw new UnsupportedOperationException("Not implemented"); - } - - public void triggerTakeover(long msId) { - ManagementServerHostVO node = new ManagementServerHostVO(); - node.setMsid(msId); - - List lst = new ArrayList(); - lst.add(node); - - _listener.onManagementNodeLeft(lst, ManagementServerNode.getManagementServerId()); - } - - protected MockClusterManager() { - } - - @Override - public boolean rebalanceAgent(long agentId, Event event, long currentOwnerId, long futureOwnerId) throws AgentUnavailableException, OperationTimedoutException { - return false; - } - - @Override - public boolean isAgentRebalanceEnabled() { - return false; - } - - @Override - public Boolean propagateResourceEvent(long agentId, com.cloud.resource.ResourceState.Event event) throws AgentUnavailableException { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean executeResourceUserRequest(long hostId, com.cloud.resource.ResourceState.Event event) throws AgentUnavailableException { - // TODO Auto-generated method stub - return false; - } - - /* (non-Javadoc) - * @see com.cloud.cluster.ClusterManager#executeAsync(java.lang.String, long, com.cloud.agent.api.Command[], boolean) - */ - @Override - public void executeAsync(String strPeer, long agentId, Command[] cmds, boolean stopOnError) { - // TODO Auto-generated method stub - - } - } - -} diff --git a/utils/src/com/cloud/utils/component/AdapterBase.java b/utils/src/com/cloud/utils/component/AdapterBase.java index e7be829bdbf..7b75993776b 100644 --- a/utils/src/com/cloud/utils/component/AdapterBase.java +++ b/utils/src/com/cloud/utils/component/AdapterBase.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.utils.component; +import java.util.List; import java.util.Map; import javax.naming.ConfigurationException; @@ -45,4 +46,12 @@ public class AdapterBase implements Adapter { return true; } + public static T getAdapterByName(List adapters, String name) { + for(T adapter : adapters) { + if(adapter.getName().equals(name)) + return adapter; + } + return null; + } + } diff --git a/utils/src/com/cloud/utils/component/Adapters.java b/utils/src/com/cloud/utils/component/Adapters.java deleted file mode 100755 index 2a2203ff555..00000000000 --- a/utils/src/com/cloud/utils/component/Adapters.java +++ /dev/null @@ -1,93 +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 -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.utils.component; - -import java.util.Collection; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.cloud.utils.EnumerationImpl; -import com.cloud.utils.component.LegacyComponentLocator.ComponentInfo; - -/** - * the iterator even during dynamic reloading. - * - **/ -public class Adapters implements Iterable { - protected Map _map; - protected List> _infos; - - protected String _name; - - public Adapters(String name, List> adapters) { - _name = name; - set(adapters); - } - - /** - * Get the adapter list name. - * - * @return the name of the list of adapters. - */ - public String getName() { - return _name; - } - - public Enumeration enumeration() { - return new EnumerationImpl(_map.values().iterator()); - } - - @Override - public Iterator iterator() { - return new EnumerationImpl(_map.values().iterator()); - } - - protected Collection get() { - return _map.values(); - } - - protected void set(List> adapters) { - HashMap map = new LinkedHashMap(adapters.size()); - for (ComponentInfo adapter : adapters) { - @SuppressWarnings("unchecked") - T t = (T)adapter.instance; - map.put(adapter.getName(), t); - } - this._map = map; - this._infos = adapters; - } - - public T get(String name) { - return _map.get(name); - } - - public boolean isSet() { - return _map.size() != 0; - } - - public static T getAdapterByName(List adapters, String name) { - for(T adapter : adapters) { - if(adapter.getName().equals(name)) - return adapter; - } - return null; - } -} diff --git a/utils/src/com/cloud/utils/component/ComponentContext.java b/utils/src/com/cloud/utils/component/ComponentContext.java index ce464232417..3fffd176898 100644 --- a/utils/src/com/cloud/utils/component/ComponentContext.java +++ b/utils/src/com/cloud/utils/component/ComponentContext.java @@ -54,12 +54,12 @@ public class ComponentContext implements ApplicationContextAware { return s_appContext; } - public static T getCompanent(String name) { + public static T getComponent(String name) { assert(s_appContext != null); return (T)s_appContext.getBean(name); } - public static T getCompanent(Class beanType) { + public static T getComponent(Class beanType) { assert(s_appContext != null); try { return (T)s_appContext.getBean(beanType); diff --git a/utils/src/com/cloud/utils/component/ComponentInject.java b/utils/src/com/cloud/utils/component/ComponentInject.java deleted file mode 100644 index c88cd3f4c3c..00000000000 --- a/utils/src/com/cloud/utils/component/ComponentInject.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.cloud.utils.component; - -import javax.inject.Inject; - -import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.stereotype.Component; - -@Component -public class ComponentInject { - - private static AutowireCapableBeanFactory beanFactory; - @SuppressWarnings("unused") - @Inject - private void setbeanFactory(AutowireCapableBeanFactory bf) { - ComponentInject.beanFactory = bf; - } - - public static T inject(Class clazz) { - return beanFactory.createBean(clazz); - } - - public static T inject(T obj) { - beanFactory.autowireBean(obj); - beanFactory.initializeBean(obj, null); - return obj; - } -} diff --git a/utils/src/com/cloud/utils/component/ComponentLibrary.java b/utils/src/com/cloud/utils/component/ComponentLibrary.java deleted file mode 100755 index 52c470389c0..00000000000 --- a/utils/src/com/cloud/utils/component/ComponentLibrary.java +++ /dev/null @@ -1,56 +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 -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.utils.component; - -import java.util.List; -import java.util.Map; - -import com.cloud.utils.component.LegacyComponentLocator.ComponentInfo; -import com.cloud.utils.db.GenericDao; - -/** - * ComponentLibrary specifies the implementation classes that a server needs - * attribute of the server element within components.xml. ComponentLocator - * first loads the implementations specified here, then, it loads the - * implementations from components.xml. If an interface is specified in both - * within the components.xml overrides the one within ComponentLibrary. - * - */ -public interface ComponentLibrary { - /** - * @return all of the daos - */ - Map>> getDaos(); - - /** - * @return all of the Managers - */ - Map> getManagers(); - - /** - * @return all of the adapters - */ - Map>> getAdapters(); - - Map, Class> getFactories(); - - /** - * @return all the services - * - */ - Map> getPluggableServices(); -} diff --git a/utils/src/com/cloud/utils/component/ComponentLibraryBase.java b/utils/src/com/cloud/utils/component/ComponentLibraryBase.java deleted file mode 100644 index 58649e425d6..00000000000 --- a/utils/src/com/cloud/utils/component/ComponentLibraryBase.java +++ /dev/null @@ -1,99 +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 -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.utils.component; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.cloud.utils.Pair; -import com.cloud.utils.component.LegacyComponentLocator.ComponentInfo; -import com.cloud.utils.db.GenericDao; - -public abstract class ComponentLibraryBase implements ComponentLibrary { - - protected final Map>> _daos = new LinkedHashMap>>(); - - protected ComponentInfo> addDao(String name, Class> clazz) { - return addDao(name, clazz, new ArrayList>(), true); - } - - protected ComponentInfo> addDao(String name, Class> clazz, List> params, boolean singleton) { - ComponentInfo> componentInfo = new ComponentInfo>(name, clazz, params, singleton); - for (String key : componentInfo.getKeys()) { - _daos.put(key, componentInfo); - } - return componentInfo; - } - - protected Map> _managers = new LinkedHashMap>(); - protected Map>> _adapters = new LinkedHashMap>>(); - protected Map> _pluggableServices = new LinkedHashMap>(); - - protected ComponentInfo addManager(String name, Class clazz, List> params, boolean singleton) { - ComponentInfo info = new ComponentInfo(name, clazz, params, singleton); - for (String key : info.getKeys()) { - _managers.put(key, info); - } - return info; - } - - protected ComponentInfo addManager(String name, Class clazz) { - return addManager(name, clazz, new ArrayList>(), true); - } - - protected List> addAdapterChain(Class interphace, List>> adapters) { - ArrayList> lst = new ArrayList>(adapters.size()); - for (Pair> adapter : adapters) { - @SuppressWarnings("unchecked") - Class clazz = (Class)adapter.second(); - lst.add(new ComponentInfo(adapter.first(), clazz)); - } - _adapters.put(interphace.getName(), lst); - return lst; - } - - protected void addAdapter(Class interphace, String name, Class adapterClass) { - List> lst = _adapters.get(interphace.getName()); - if (lst == null) { - addOneAdapter(interphace, name, adapterClass); - } else { - @SuppressWarnings("unchecked") - Class clazz = (Class)adapterClass; - lst.add(new ComponentInfo(name, clazz)); - } - } - - protected ComponentInfo addOneAdapter(Class interphace, String name, Class adapterClass) { - List>> adapters = new ArrayList>>(); - adapters.add(new Pair>(name, adapterClass)); - return addAdapterChain(interphace, adapters).get(0); - } - - - protected ComponentInfo addService(String name, Class serviceInterphace, Class clazz, List> params, boolean singleton) { - ComponentInfo info = new ComponentInfo(name, clazz, params, singleton); - _pluggableServices.put(serviceInterphace.getName(), info); - return info; - } - - protected ComponentInfo addService(String name, Class serviceInterphace, Class clazz) { - return addService(name, serviceInterphace, clazz, new ArrayList>(), true); - } - } diff --git a/utils/src/com/cloud/utils/component/ComponentLocator.java b/utils/src/com/cloud/utils/component/ComponentLocator.java deleted file mode 100644 index d8d6e63b199..00000000000 --- a/utils/src/com/cloud/utils/component/ComponentLocator.java +++ /dev/null @@ -1,58 +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 -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.utils.component; - -import java.io.Serializable; - -import org.springframework.stereotype.Component; - -import com.cloud.utils.db.GenericDao; - -@Component -public class ComponentLocator { - public static ComponentLocator getCurrentLocator() { - return ComponentContext.getCompanent(ComponentLocator.class); - } - - public static ComponentLocator getLocator(String server) { - return ComponentContext.getCompanent(ComponentLocator.class); - } - - public static ComponentLocator getLocator(String server, String configFileName, String log4jFilename) { - return ComponentContext.getCompanent(ComponentLocator.class); - } - - public static Object getComponent(String componentName) { - return ComponentContext.getCompanent(componentName); - } - - public > T getDao(Class clazz) { - return ComponentContext.getCompanent(clazz); - } - - public T getManager(Class clazz) { - return ComponentContext.getCompanent(clazz); - } - - public T getPluggableService(Class clazz) { - return ComponentContext.getCompanent(clazz); - } - - public static T inject(Class clazz) { - return ComponentContext.inject(clazz); - } -} diff --git a/utils/src/com/cloud/utils/component/ComponentLocatorMBean.java b/utils/src/com/cloud/utils/component/ComponentLocatorMBean.java deleted file mode 100755 index 125e92ac5d9..00000000000 --- a/utils/src/com/cloud/utils/component/ComponentLocatorMBean.java +++ /dev/null @@ -1,43 +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 -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.utils.component; - - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import com.cloud.utils.mgmt.ManagementBean; - -public interface ComponentLocatorMBean extends ManagementBean { - - /** - * @return the list of adapters accessible by this component locator. - **/ - Map> getAdapterNames(); - - /** - * @return the list of managers accessible by this component locator. - **/ - Collection getManagerNames(); - - /** - * @return the list of DAOs accessible by this component locator. - */ - Collection getDaoNames(); - -} diff --git a/utils/src/com/cloud/utils/component/Inject.java b/utils/src/com/cloud/utils/component/Inject.java deleted file mode 100644 index 50c890da75b..00000000000 --- a/utils/src/com/cloud/utils/component/Inject.java +++ /dev/null @@ -1,30 +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 -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.utils.component; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -@Target(FIELD) -@Retention(RUNTIME) -public @interface Inject { - Class adapter() default Adapter.class; -} - diff --git a/utils/src/com/cloud/utils/component/LegacyComponentLocator.java b/utils/src/com/cloud/utils/component/LegacyComponentLocator.java deleted file mode 100755 index 719f5601456..00000000000 --- a/utils/src/com/cloud/utils/component/LegacyComponentLocator.java +++ /dev/null @@ -1,1282 +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 -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.utils.component; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.Serializable; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import javax.ejb.Local; -import javax.inject.Inject; -import javax.management.InstanceAlreadyExistsException; -import javax.management.MBeanRegistrationException; -import javax.management.MalformedObjectNameException; -import javax.management.NotCompliantMBeanException; -import javax.naming.ConfigurationException; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import net.sf.cglib.proxy.Callback; -import net.sf.cglib.proxy.CallbackFilter; -import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.Factory; -import net.sf.cglib.proxy.MethodInterceptor; -import net.sf.cglib.proxy.MethodProxy; -import net.sf.cglib.proxy.NoOp; - -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; -import org.apache.log4j.xml.DOMConfigurator; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import com.cloud.utils.Pair; -import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.db.DatabaseCallback; -import com.cloud.utils.db.DatabaseCallbackFilter; -import com.cloud.utils.db.GenericDao; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.mgmt.JmxUtil; -import com.cloud.utils.mgmt.ManagementBean; - -/** - * ComponentLocator ties together several different concepts. First, it - * deals with how a system should be put together. It manages different - * types of components: - * - Manager: Singleton implementation of a certain process. - * - Adapter: Different singleton implementations for the same functions. - * - SystemIntegrityChecker: Singletons that are called at the load time. - * - Dao: Data Access Objects. - * - * These components can be declared in several ways: - * - ComponentLibrary - A Java class that declares the above components. The - * advantage of declaring components here is they change automatically - * with any refactoring. - * - components specification - An xml file that overrides the - * ComponentLibrary. The advantage of declaring components here is - * they can change by hand on every deployment. - * - * The two are NOT mutually exclusive. ComponentLocator basically locates - * the components specification, which specifies the ComponentLibrary within. - * Components found in the ComponentLibrary are overridden by components - * found in components specification. - * - * Components specification can also be nested. One components specification - * can point to another components specification and, therefore, "inherits" - * those components but still override one or more components. ComponentLocator - * reads the child components specification first and follow the chain up. - * the child's components overrides the ones in the parent. - * - * ComponentLocator looks for the components specification as follows: - * 1. By following the path specified by "cloud-stack-components-specification" - * within the environment.properties file. - * 2. Look for components.xml in the class path. - * - * ComponentLocator also ties in component injection. Components can specify - * an @Inject annotation to components ComponentLocator knows. When - * instantiating components, ComponentLocator attempts to inject these - * components. - * - **/ -@SuppressWarnings("unchecked") -public class LegacyComponentLocator implements ComponentLocatorMBean { - protected static final Logger s_logger = Logger.getLogger(LegacyComponentLocator.class); - - protected static final ThreadLocal s_tl = new ThreadLocal(); - protected static final ConcurrentHashMap, Singleton> s_singletons = new ConcurrentHashMap, Singleton>(111); - protected static final HashMap s_locators = new HashMap(); - protected static final HashMap, InjectInfo> s_factories = new HashMap, InjectInfo>(); - protected static Boolean s_once = false; - protected static Boolean _hasCheckerRun = false; - protected static Callback[] s_callbacks = new Callback[] { NoOp.INSTANCE, new DatabaseCallback()}; - protected static CallbackFilter s_callbackFilter = new DatabaseCallbackFilter(); - protected static final List> s_interceptors = new ArrayList>(); - protected static CleanupThread s_janitor = null; - - protected HashMap> _adapterMap; - protected HashMap> _managerMap; - protected LinkedHashMap> _checkerMap; - protected LinkedHashMap>> _daoMap; - protected String _serverName; - protected Object _component; - protected HashMap, Class> _factories; - protected HashMap> _pluginsMap; - - static { - if (s_janitor == null) { - s_janitor = new CleanupThread(); - Runtime.getRuntime().addShutdownHook(new CleanupThread()); - } - } - - public LegacyComponentLocator(String server) { - _serverName = server; - if (s_janitor == null) { - s_janitor = new CleanupThread(); - Runtime.getRuntime().addShutdownHook(new CleanupThread()); - } - } - - public String getLocatorName() { - return _serverName; - } - - @Override - public String getName() { - return getLocatorName(); - } - - protected Pair>>> parse2(String filename) { - try { - SAXParserFactory spfactory = SAXParserFactory.newInstance(); - SAXParser saxParser = spfactory.newSAXParser(); - _daoMap = new LinkedHashMap>>(); - _managerMap = new LinkedHashMap>(); - _checkerMap = new LinkedHashMap>(); - _adapterMap = new HashMap>(); - _factories = new HashMap, Class>(); - _pluginsMap = new LinkedHashMap>(); - File file = PropertiesUtil.findConfigFile(filename); - if (file == null) { - s_logger.info("Unable to find " + filename); - return null; - } - s_logger.info("Config file found at " + file.getAbsolutePath() + ". Configuring " + _serverName); - XmlHandler handler = new XmlHandler(_serverName); - saxParser.parse(file, handler); - - HashMap>> adapters = new HashMap>>(); - if (handler.parent != null) { - String[] tokens = handler.parent.split(":"); - String parentFile = filename; - String parentName = handler.parent; - if (tokens.length > 1) { - parentFile = tokens[0]; - parentName = tokens[1]; - } - LegacyComponentLocator parentLocator = new LegacyComponentLocator(parentName); - adapters.putAll(parentLocator.parse2(parentFile).second()); - _daoMap.putAll(parentLocator._daoMap); - _managerMap.putAll(parentLocator._managerMap); - _factories.putAll(parentLocator._factories); - _pluginsMap.putAll(parentLocator._pluginsMap); - } - - ComponentLibrary library = null; - if (handler.library != null) { - Class clazz = Class.forName(handler.library); - library = (ComponentLibrary)clazz.newInstance(); - _daoMap.putAll(library.getDaos()); - _managerMap.putAll(library.getManagers()); - adapters.putAll(library.getAdapters()); - _factories.putAll(library.getFactories()); - _pluginsMap.putAll(library.getPluggableServices()); - } - - _daoMap.putAll(handler.daos); - _managerMap.putAll(handler.managers); - _checkerMap.putAll(handler.checkers); - adapters.putAll(handler.adapters); - _pluginsMap.putAll(handler.pluggableServices); - - return new Pair>>>(handler, adapters); - } catch (ParserConfigurationException e) { - s_logger.error("Unable to load " + _serverName + " due to errors while parsing " + filename, e); - System.exit(1); - } catch (SAXException e) { - s_logger.error("Unable to load " + _serverName + " due to errors while parsing " + filename, e); - System.exit(1); - } catch (IOException e) { - s_logger.error("Unable to load " + _serverName + " due to errors while reading from " + filename, e); - System.exit(1); - } catch (CloudRuntimeException e) { - s_logger.error("Unable to load configuration for " + _serverName + " from " + filename, e); - System.exit(1); - } catch (Exception e) { - s_logger.error("Unable to load configuration for " + _serverName + " from " + filename, e); - System.exit(1); - } - return null; - } - - protected void parse(String filename) { - Pair>>> result = parse2(filename); - if (result == null) { - s_logger.info("Skipping configuration using " + filename); - return; - } - - instantiatePluggableServices(); - - XmlHandler handler = result.first(); - HashMap>> adapters = result.second(); - try { - runCheckers(); - startDaos(); // daos should not be using managers and adapters. - instantiateAdapters(adapters); - instantiateManagers(); - if (handler.componentClass != null) { - _component = createInstance(handler.componentClass, true, true); - } - configureManagers(); - configureAdapters(); - startManagers(); - startAdapters(); - //TODO do we need to follow the instantiate -> inject -> configure -> start -> stop flow of singletons like managers/adapters? - //TODO do we need to expose pluggableServices to MBean (provide getNames?) - } catch (CloudRuntimeException e) { - s_logger.error("Unable to load configuration for " + _serverName + " from " + filename, e); - System.exit(1); - } catch (Exception e) { - s_logger.error("Unable to load configuration for " + _serverName + " from " + filename, e); - System.exit(1); - } - } - - protected void runCheckers() { - Set>> entries = _checkerMap.entrySet(); - for (Map.Entry> entry : entries) { - ComponentInfo info = entry.getValue(); - try { - info.instance = (SystemIntegrityChecker)createInstance(info.clazz, false, info.singleton); - info.instance.check(); - } catch (Exception e) { - s_logger.error("Problems with running checker:" + info.name, e); - System.exit(1); - } - } - } - /** - * Daos should not refer to any other components so it is safe to start them - * here. - */ - protected void startDaos() { - Set>>> entries = _daoMap.entrySet(); - - for (Map.Entry>> entry : entries) { - ComponentInfo> info = entry.getValue(); - try { - info.instance = (GenericDao)createInstance(info.clazz, true, info.singleton); - if (info.singleton) { - s_logger.info("Starting singleton DAO: " + info.name); - Singleton singleton = s_singletons.get(info.clazz); - if (singleton.state == Singleton.State.Instantiated) { - inject(info.clazz, info.instance); - singleton.state = Singleton.State.Injected; - } - if (singleton.state == Singleton.State.Injected) { - if (!info.instance.configure(info.name, info.params)) { - s_logger.error("Unable to configure DAO: " + info.name); - System.exit(1); - } - singleton.state = Singleton.State.Started; - } - } else { - s_logger.info("Starting DAO: " + info.name); - inject(info.clazz, info.instance); - if (!info.instance.configure(info.name, info.params)) { - s_logger.error("Unable to configure DAO: " + info.name); - System.exit(1); - } - } - } catch (ConfigurationException e) { - s_logger.error("Unable to configure DAO: " + info.name, e); - System.exit(1); - } catch (Exception e) { - s_logger.error("Problems while configuring DAO: " + info.name, e); - System.exit(1); - } - if (info.instance instanceof ManagementBean) { - registerMBean((ManagementBean) info.instance); - } - } - } - - private static Object createInstance(Class clazz, boolean inject, boolean singleton, Object... args) { - Factory factory = null; - Singleton entity = null; - synchronized(s_factories) { - if (singleton) { - entity = s_singletons.get(clazz); - if (entity != null) { - s_logger.debug("Found singleton instantiation for " + clazz.toString()); - return entity.singleton; - } - } - InjectInfo info = s_factories.get(clazz); - if (info == null) { - Enhancer enhancer = new Enhancer(); - enhancer.setSuperclass(clazz); - enhancer.setCallbackFilter(s_callbackFilter); - enhancer.setCallbacks(s_callbacks); - factory = (Factory)enhancer.create(); - info = new InjectInfo(enhancer, factory); - s_factories.put(clazz, info); - } else { - factory = info.factory; - } - } - - - Class[] argTypes = null; - if (args != null && args.length > 0) { - Constructor[] constructors = clazz.getConstructors(); - for (Constructor constructor : constructors) { - Class[] paramTypes = constructor.getParameterTypes(); - if (paramTypes.length == args.length) { - boolean found = true; - for (int i = 0; i < paramTypes.length; i++) { - if (!paramTypes[i].isAssignableFrom(args[i].getClass()) && !paramTypes[i].isPrimitive()) { - found = false; - break; - } - } - if (found) { - argTypes = paramTypes; - break; - } - } - } - - if (argTypes == null) { - throw new CloudRuntimeException("Unable to find constructor to match parameters given: " + clazz.getName()); - } - - entity = new Singleton(factory.newInstance(argTypes, args, s_callbacks)); - } else { - entity = new Singleton(factory.newInstance(s_callbacks)); - } - - if (inject) { - inject(clazz, entity.singleton); - entity.state = Singleton.State.Injected; - } - - if (singleton) { - synchronized(s_factories) { - s_singletons.put(clazz, entity); - } - } - - return entity.singleton; - } - - - protected ComponentInfo> getDao(String name) { - ComponentInfo> info = _daoMap.get(name); - if (info == null) { - throw new CloudRuntimeException("Unable to find DAO " + name); - } - - return info; - } - - public static synchronized Object getComponent(String componentName) { - synchronized(_hasCheckerRun) { - /* System Integrity checker will run before all components really loaded */ - if (!_hasCheckerRun && !componentName.equalsIgnoreCase(SystemIntegrityChecker.Name)) { - LegacyComponentLocator.getComponent(SystemIntegrityChecker.Name); - _hasCheckerRun = true; - } - } - - LegacyComponentLocator locator = s_locators.get(componentName); - if (locator == null) { - locator = LegacyComponentLocator.getLocator(componentName); - } - return locator._component; - } - - public > T getDao(Class clazz) { - ComponentInfo> info = getDao(clazz.getName()); - return info != null ? (T)info.instance : null; - } - - protected void instantiateManagers() { - Set>> entries = _managerMap.entrySet(); - for (Map.Entry> entry : entries) { - ComponentInfo info = entry.getValue(); - if (info.instance == null) { - s_logger.info("Instantiating Manager: " + info.name); - info.instance = (Manager)createInstance(info.clazz, false, info.singleton); - } - } - } - - protected void configureManagers() { - Set>> entries = _managerMap.entrySet(); - for (Map.Entry> entry : entries) { - ComponentInfo info = entry.getValue(); - if (info.singleton) { - Singleton s = s_singletons.get(info.clazz); - if (s.state == Singleton.State.Instantiated) { - s_logger.debug("Injecting singleton Manager: " + info.name); - inject(info.clazz, info.instance); - s.state = Singleton.State.Injected; - } - } else { - s_logger.info("Injecting Manager: " + info.name); - inject(info.clazz, info.instance); - } - } - for (Map.Entry> entry : entries) { - ComponentInfo info = entry.getValue(); - if (info.singleton) { - Singleton s = s_singletons.get(info.clazz); - if (s.state == Singleton.State.Injected) { - s_logger.info("Configuring singleton Manager: " + info.name); - try { - info.instance.configure(info.name, info.params); - } catch (ConfigurationException e) { - s_logger.error("Unable to configure manager: " + info.name, e); - System.exit(1); - } - s.state = Singleton.State.Configured; - } - } else { - s_logger.info("Configuring Manager: " + info.name); - try { - info.instance.configure(info.name, info.params); - } catch (ConfigurationException e) { - s_logger.error("Unable to configure manager: " + info.name, e); - System.exit(1); - } - } - } - } - - protected static void inject(Class clazz, Object entity) { - LegacyComponentLocator locator = LegacyComponentLocator.getCurrentLocator(); - - do { - Field[] fields = clazz.getDeclaredFields(); - for (Field field : fields) { - Inject inject = field.getAnnotation(Inject.class); - if (inject == null) { - com.cloud.utils.component.Inject oldInject = field.getAnnotation(com.cloud.utils.component.Inject.class); - if(inject != null) { - Class fc = field.getType(); - Object instance = null; - if (Manager.class.isAssignableFrom(fc)) { - s_logger.trace("Manager: " + fc.getName()); - instance = locator.getManager(fc); - } else if (GenericDao.class.isAssignableFrom(fc)) { - s_logger.trace("Dao:" + fc.getName()); - instance = locator.getDao((Class>)fc); - } else if (Adapters.class.isAssignableFrom(fc)) { - s_logger.trace("Adapter" + fc.getName()); - instance = locator.getAdapters(oldInject.adapter()); - } else { - s_logger.trace("Other:" + fc.getName()); - instance = locator.getManager(fc); - } - } - - continue; - } - - Class fc = field.getType(); - Object instance = null; - if (Manager.class.isAssignableFrom(fc)) { - s_logger.trace("Manager: " + fc.getName()); - instance = locator.getManager(fc); - } else if (GenericDao.class.isAssignableFrom(fc)) { - s_logger.trace("Dao:" + fc.getName()); - instance = locator.getDao((Class>)fc); - } else { - s_logger.trace("Other:" + fc.getName()); - instance = locator.getManager(fc); - } - - if (instance == null) { - throw new CloudRuntimeException("Unable to inject " + fc.getSimpleName() + " in " + clazz.getSimpleName()); - } - - try { - field.setAccessible(true); - field.set(entity, instance); - } catch (IllegalArgumentException e) { - throw new CloudRuntimeException("hmmm....is it really illegal?", e); - } catch (IllegalAccessException e) { - throw new CloudRuntimeException("what! what ! what!", e); - } - } - clazz = clazz.getSuperclass(); - } while (clazz != Object.class && clazz != null); - } - - protected void startManagers() { - Set>> entries = _managerMap.entrySet(); - for (Map.Entry> entry : entries) { - ComponentInfo info = entry.getValue(); - if (info.singleton) { - Singleton s = s_singletons.get(info.clazz); - if (s.state == Singleton.State.Configured) { - s_logger.info("Starting singleton Manager: " + info.name); - if (!info.instance.start()) { - throw new CloudRuntimeException("Incorrect Configuration: " + info.name); - } - if (info.instance instanceof ManagementBean) { - registerMBean((ManagementBean) info.instance); - } - s_logger.info("Started Manager: " + info.name); - s.state = Singleton.State.Started; - } - } else { - s_logger.info("Starting Manager: " + info.name); - if (!info.instance.start()) { - throw new CloudRuntimeException("Incorrect Configuration: " + info.name); - } - if (info.instance instanceof ManagementBean) { - registerMBean((ManagementBean) info.instance); - } - s_logger.info("Started Manager: " + info.name); - } - } - } - - protected void registerMBean(ManagementBean mbean) { - try { - JmxUtil.registerMBean(mbean); - } catch (MalformedObjectNameException e) { - s_logger.warn("Unable to register MBean: " + mbean.getName(), e); - } catch (InstanceAlreadyExistsException e) { - s_logger.warn("Unable to register MBean: " + mbean.getName(), e); - } catch (MBeanRegistrationException e) { - s_logger.warn("Unable to register MBean: " + mbean.getName(), e); - } catch (NotCompliantMBeanException e) { - s_logger.warn("Unable to register MBean: " + mbean.getName(), e); - } - s_logger.info("Registered MBean: " + mbean.getName()); - } - - protected ComponentInfo getManager(String name) { - ComponentInfo mgr = _managerMap.get(name); - return mgr; - } - - public T getManager(Class clazz) { - ComponentInfo info = getManager(clazz.getName()); - if (info == null) { - return null; - } - if (info.instance == null) { - info.instance = (Manager)createInstance(info.clazz, false, info.singleton); - } - return (T)info.instance; - } - - protected void configureAdapters() { - for (Adapters adapters : _adapterMap.values()) { - List> infos = adapters._infos; - for (ComponentInfo info : infos) { - try { - if (info.singleton) { - Singleton singleton = s_singletons.get(info.clazz); - if (singleton.state == Singleton.State.Instantiated) { - s_logger.info("Injecting singleton Adapter: " + info.getName()); - inject(info.clazz, info.instance); - singleton.state = Singleton.State.Injected; - } - if (singleton.state == Singleton.State.Injected) { - s_logger.info("Configuring singleton Adapter: " + info.getName()); - if (!info.instance.configure(info.name, info.params)) { - s_logger.error("Unable to configure adapter: " + info.name); - System.exit(1); - } - singleton.state = Singleton.State.Configured; - } - } else { - s_logger.info("Injecting Adapter: " + info.getName()); - inject(info.clazz, info.instance); - s_logger.info("Configuring singleton Adapter: " + info.getName()); - if (!info.instance.configure(info.name, info.params)) { - s_logger.error("Unable to configure adapter: " + info.name); - System.exit(1); - } - } - } catch (ConfigurationException e) { - s_logger.error("Unable to configure adapter: " + info.name, e); - System.exit(1); - } catch (Exception e) { - s_logger.error("Unable to configure adapter: " + info.name, e); - System.exit(1); - } - } - } - } - - protected void populateAdapters(Map>> map) { - Set>>> entries = map.entrySet(); - for (Map.Entry>> entry : entries) { - for (ComponentInfo info : entry.getValue()) { - s_logger.info("Instantiating Adapter: " + info.name); - info.instance = (Adapter)createInstance(info.clazz, false, info.singleton); - } - Adapters adapters = new Adapters(entry.getKey(), entry.getValue()); - _adapterMap.put(entry.getKey(), adapters); - } - } - - protected void instantiateAdapters(Map>> map) { - Set>>> entries = map.entrySet(); - for (Map.Entry>> entry : entries) { - for (ComponentInfo info : entry.getValue()) { - s_logger.info("Instantiating Adapter: " + info.name); - info.instance = (Adapter)createInstance(info.clazz, false, info.singleton); - } - Adapters adapters = new Adapters(entry.getKey(), entry.getValue()); - _adapterMap.put(entry.getKey(), adapters); - } - } - - protected void startAdapters() { - for (Map.Entry> entry : _adapterMap.entrySet()) { - for (ComponentInfo adapter : entry.getValue()._infos) { - if (adapter.singleton) { - Singleton s = s_singletons.get(adapter.clazz); - if (s.state == Singleton.State.Configured) { - s_logger.info("Starting singleton Adapter: " + adapter.getName()); - if (!adapter.instance.start()) { - throw new CloudRuntimeException("Unable to start adapter: " + adapter.getName()); - } - if (adapter.instance instanceof ManagementBean) { - registerMBean((ManagementBean)adapter.instance); - } - s_logger.info("Started Adapter: " + adapter.instance.getName()); - } - s.state = Singleton.State.Started; - } else { - s_logger.info("Starting Adapter: " + adapter.getName()); - if (!adapter.instance.start()) { - throw new CloudRuntimeException("Unable to start adapter: " + adapter.getName()); - } - if (adapter.instance instanceof ManagementBean) { - registerMBean((ManagementBean)adapter.instance); - } - s_logger.info("Started Adapter: " + adapter.instance.getName()); - } - } - } - } - - protected void instantiatePluggableServices() { - Set>> entries = _pluginsMap.entrySet(); - for (Map.Entry> entry : entries) { - ComponentInfo info = entry.getValue(); - if (info.instance == null) { - s_logger.info("Instantiating PluggableService: " + info.name); - info.instance = (PluggableService)createInstance(info.clazz, false, info.singleton); - - if (info.instance instanceof Plugin) { - Plugin plugin = (Plugin)info.instance; - - ComponentLibrary lib = plugin.getComponentLibrary(); - _managerMap.putAll(lib.getManagers()); - _daoMap.putAll(lib.getDaos()); - } - } - } - } - - protected ComponentInfo getPluggableService(String name) { - ComponentInfo mgr = _pluginsMap.get(name); - return mgr; - } - - public T getPluggableService(Class clazz) { - ComponentInfo info = getPluggableService(clazz.getName()); - if (info == null) { - return null; - } - if (info.instance == null) { - info.instance = (PluggableService)createInstance(info.clazz, false, info.singleton); - } - return (T)info.instance; - } - - public List getAllPluggableServices() { - List services = new ArrayList(); - Set>> entries = _pluginsMap.entrySet(); - for (Map.Entry> entry : entries) { - ComponentInfo info = entry.getValue(); - if (info.instance == null) { - s_logger.info("Instantiating PluggableService: " + info.name); - info.instance = (PluggableService)createInstance(info.clazz, false, info.singleton); - } - services.add((T) info.instance); - } - return services; - } - - public static T inject(Class clazz) { - return (T)createInstance(clazz, true, false); - } - - public T createInstance(Class clazz) { - Class impl = (Class)_factories.get(clazz); - if (impl == null) { - throw new CloudRuntimeException("Unable to find a factory for " + clazz); - } - return inject(impl); - } - - public static T inject(Class clazz, Object... args) { - return (T)createInstance(clazz, true, false, args); - } - - @Override - public Map> getAdapterNames() { - HashMap> result = new HashMap>(); - for (Map.Entry> entry : _adapterMap.entrySet()) { - Adapters adapters = entry.getValue(); - Enumeration en = adapters.enumeration(); - List lst = new ArrayList(); - while (en.hasMoreElements()) { - Adapter adapter = en.nextElement(); - lst.add(adapter.getName() + "-" + adapter.getClass().getName()); - } - result.put(entry.getKey(), lst); - } - return result; - } - - public Map> getAllAccessibleAdapters() { - Map> parentResults = new HashMap>(); - Map> results = getAdapterNames(); - parentResults.putAll(results); - return parentResults; - } - - @Override - public Collection getManagerNames() { - Collection names = new HashSet(); - for (Map.Entry> entry : _managerMap.entrySet()) { - names.add(entry.getValue().name); - } - return names; - } - - @Override - public Collection getDaoNames() { - Collection names = new HashSet(); - for (Map.Entry>> entry : _daoMap.entrySet()) { - names.add(entry.getValue().name); - } - return names; - } - - public Adapters getAdapters(Class clazz) { - return (Adapters)getAdapters(clazz.getName()); - } - - public Adapters getAdapters(String key) { - Adapters adapters = _adapterMap.get(key); - if (adapters != null) { - return adapters; - } - return new Adapters(key, new ArrayList>()); - } - - protected void resetInterceptors(InterceptorLibrary library) { - library.addInterceptors(s_interceptors); - if (s_interceptors.size() > 0) { - s_callbacks = new Callback[s_interceptors.size() + 2]; - int i = 0; - s_callbacks[i++] = NoOp.INSTANCE; - s_callbacks[i++] = new InterceptorDispatcher(); - for (AnnotationInterceptor interceptor : s_interceptors) { - s_callbacks[i++] = interceptor.getCallback(); - } - s_callbackFilter = new InterceptorFilter(); - } - } - - protected static LegacyComponentLocator getLocatorInternal(String server, boolean setInThreadLocal, String configFileName, String log4jFilename) { - synchronized(s_once) { - if (!s_once) { - File file = PropertiesUtil.findConfigFile(log4jFilename + ".xml"); - if (file != null) { - s_logger.info("log4j configuration found at " + file.getAbsolutePath()); - DOMConfigurator.configureAndWatch(file.getAbsolutePath()); - } else { - file = PropertiesUtil.findConfigFile(log4jFilename + ".properties"); - if (file != null) { - s_logger.info("log4j configuration found at " + file.getAbsolutePath()); - PropertyConfigurator.configureAndWatch(file.getAbsolutePath()); - } - } - s_once = true; - } - } - - LegacyComponentLocator locator; - synchronized (s_locators) { - locator = s_locators.get(server); - if (locator == null) { - locator = new LegacyComponentLocator(server); - s_locators.put(server, locator); - if (setInThreadLocal) { - s_tl.set(locator); - } - locator.parse(configFileName); - } else { - if (setInThreadLocal) { - s_tl.set(locator); - } - } - } - - return locator; - } - - public static LegacyComponentLocator getLocator(String server, String configFileName, String log4jFilename) { - return getLocatorInternal(server, true, configFileName, log4jFilename); - } - - public static LegacyComponentLocator getLocator(String server) { - String configFile = null; - try { - final File propsFile = PropertiesUtil.findConfigFile("environment.properties"); - if (propsFile == null) { - s_logger.debug("environment.properties could not be opened"); - } else { - final FileInputStream finputstream = new FileInputStream(propsFile); - final Properties props = new Properties(); - props.load(finputstream); - finputstream.close(); - configFile = props.getProperty("cloud-stack-components-specification"); - } - } catch (IOException e) { - s_logger.debug("environment.properties could not be loaded:" + e.toString()); - } - - if (configFile == null || PropertiesUtil.findConfigFile(configFile) == null) { - configFile = "components.xml"; - if (PropertiesUtil.findConfigFile(configFile) == null){ - s_logger.debug("Can not find components.xml"); - } - } - return getLocatorInternal(server, true, configFile, "log4j-cloud"); - } - - public static LegacyComponentLocator getCurrentLocator() { - return s_tl.get(); - } - - public static class ComponentInfo { - Class clazz; - HashMap params = new HashMap(); - String name; - List keys = new ArrayList(); - T instance; - boolean singleton = true; - - protected ComponentInfo() { - } - - public List getKeys() { - return keys; - } - - public String getName() { - return name; - } - - public ComponentInfo(String name, Class clazz) { - this(name, clazz, new ArrayList>(0)); - } - - public ComponentInfo(String name, Class clazz, T instance) { - this(name, clazz); - this.instance = instance; - } - - public ComponentInfo(String name, Class clazz, List> params) { - this(name, clazz, params, true); - } - - public ComponentInfo(String name, Class clazz, List> params, boolean singleton) { - this.name = name; - this.clazz = clazz; - this.singleton = singleton; - for (Pair param : params) { - this.params.put(param.first(), param.second()); - } - fillInfo(); - } - - protected void fillInfo() { - String clazzName = clazz.getName(); - - Local local = clazz.getAnnotation(Local.class); - if (local == null) { - throw new CloudRuntimeException("Unable to find Local annotation for class " + clazzName); - } - - // Verify that all interfaces specified in the Local annotation is implemented by the class. - Class[] classes = local.value(); - for (int i = 0; i < classes.length; i++) { - if (!classes[i].isInterface()) { - throw new CloudRuntimeException(classes[i].getName() + " is not an interface"); - } - if (classes[i].isAssignableFrom(clazz)) { - keys.add(classes[i].getName()); - s_logger.info("Found component: " + classes[i].getName() + " in " + clazzName + " - " + name); - } else { - throw new CloudRuntimeException(classes[i].getName() + " is not implemented by " + clazzName); - } - } - } - - public void addParameter(String name, String value) { - params.put(name, value); - } - } - - /** - * XmlHandler is used by AdapterManager to handle the SAX parser callbacks. - * It builds a hash map of lists of adapters and a hash map of managers. - **/ - protected class XmlHandler extends DefaultHandler { - public HashMap>> adapters; - public HashMap> managers; - public LinkedHashMap> checkers; - public LinkedHashMap>> daos; - public HashMap> pluggableServices; - public String parent; - public String library; - - List> lst; - String paramName; - StringBuilder value; - String serverName; - boolean parse; - ComponentInfo currentInfo; - Class componentClass; - - public XmlHandler(String serverName) { - this.serverName = serverName; - parse = false; - adapters = new HashMap>>(); - managers = new HashMap>(); - checkers = new LinkedHashMap>(); - daos = new LinkedHashMap>>(); - pluggableServices = new HashMap>(); - value = null; - parent = null; - } - - protected void fillInfo(Attributes atts, Class interphace, ComponentInfo info) { - String clazzName = getAttribute(atts, "class"); - if (clazzName == null) { - throw new CloudRuntimeException("Missing class attribute for " + interphace.getName()); - } - info.name = getAttribute(atts, "name"); - if (info.name == null) { - throw new CloudRuntimeException("Missing name attribute for " + interphace.getName()); - } - s_logger.debug("Looking for class " + clazzName); - try { - info.clazz = Class.forName(clazzName); - } catch (ClassNotFoundException e) { - throw new CloudRuntimeException("Unable to find class: " + clazzName); - } catch (Throwable e) { - throw new CloudRuntimeException("Caught throwable: ", e); - } - - if (!interphace.isAssignableFrom(info.clazz)) { - throw new CloudRuntimeException("Class " + info.clazz.toString() + " does not implment " + interphace); - } - String singleton = getAttribute(atts, "singleton"); - if (singleton != null) { - info.singleton = Boolean.parseBoolean(singleton); - } - - info.fillInfo(); - } - - @Override - public void startElement(String namespaceURI, String localName, String qName, Attributes atts) - throws SAXException { - if (qName.equals("interceptor") && s_interceptors.size() == 0) { - synchronized(s_interceptors){ - if (s_interceptors.size() == 0) { - String libraryName = getAttribute(atts, "library"); - try { - Class libraryClazz = Class.forName(libraryName); - InterceptorLibrary library = (InterceptorLibrary)libraryClazz.newInstance(); - resetInterceptors(library); - } catch (ClassNotFoundException e) { - throw new CloudRuntimeException("Unable to find " + libraryName, e); - } catch (InstantiationException e) { - throw new CloudRuntimeException("Unable to instantiate " + libraryName, e); - } catch (IllegalAccessException e) { - throw new CloudRuntimeException("Illegal access " + libraryName, e); - } - } - } - } - if (!parse) { - if (qName.equals(_serverName)) { - parse = true; - parent = getAttribute(atts, "extends"); - String implementationClass = getAttribute(atts, "class"); - if (implementationClass != null) { - try { - componentClass = Class.forName(implementationClass); - } catch (ClassNotFoundException e) { - throw new CloudRuntimeException("Unable to find " + implementationClass, e); - } - } - - library = getAttribute(atts, "library"); - } - } else if (qName.equals("adapters")) { - lst = new ArrayList>(); - String key = getAttribute(atts, "key"); - if (key == null) { - throw new CloudRuntimeException("Missing key attribute for adapters"); - } - adapters.put(key, lst); - } else if (qName.equals("adapter")) { - ComponentInfo info = new ComponentInfo(); - fillInfo(atts, Adapter.class, info); - lst.add(info); - currentInfo = info; - } else if (qName.equals("manager")) { - ComponentInfo info = new ComponentInfo(); - fillInfo(atts, Manager.class, info); - s_logger.info("Adding Manager: " + info.name); - for (String key : info.keys) { - s_logger.info("Linking " + key + " to " + info.name); - managers.put(key, info); - } - currentInfo = info; - } else if (qName.equals("param")) { - paramName = getAttribute(atts, "name"); - value = new StringBuilder(); - } else if (qName.equals("dao")) { - ComponentInfo> info = new ComponentInfo>(); - fillInfo(atts, GenericDao.class, info); - for (String key : info.keys) { - daos.put(key, info); - } - currentInfo = info; - } else if (qName.equals("checker")) { - ComponentInfo info = new ComponentInfo(); - fillInfo(atts, SystemIntegrityChecker.class, info); - checkers.put(info.name, info); - s_logger.info("Adding system integrity checker: " + info.name); - currentInfo = info; - } else if (qName.equals("pluggableservice") || qName.equals("plugin")) { - ComponentInfo info = new ComponentInfo(); - fillInfo(atts, PluggableService.class, info); - s_logger.info("Adding PluggableService: " + info.name); - String key = getAttribute(atts, "key"); - if (key == null) { - throw new CloudRuntimeException("Missing key attribute for pluggableservice: "+info.name); - } - s_logger.info("Linking " + key + " to " + info.name); - pluggableServices.put(key, info); - currentInfo = info; - } else { - // ignore - } - } - - protected String getAttribute(Attributes atts, String name) { - for (int att = 0; att < atts.getLength(); att++) { - String attName = atts.getQName(att); - if (attName.equals(name)) { - return atts.getValue(att); - } - } - return null; - } - - @Override - public void endElement(String namespaceURI, String localName, String qName) throws SAXException { - if (!parse) { - return; - } - - if (qName.equals(_serverName)) { - parse = false; - } else if (qName.equals("adapters")) { - } else if (qName.equals("adapter")) { - } else if (qName.equals("manager")) { - } else if (qName.equals("dao")) { - } else if (qName.equals("pluggableservice")) { - } else if (qName.equals("param")) { - currentInfo.params.put(paramName, value.toString()); - paramName = null; - value = null; - } else { - // ignore - } - } - - @Override - public void characters(char[] ch, int start, int length) throws SAXException { - if (parse && value != null) { - value.append(ch, start, length); - } - } - } - - protected static class InjectInfo { - public Factory factory; - public Enhancer enhancer; - - public InjectInfo(Enhancer enhancer, Factory factory) { - this.factory = factory; - this.enhancer = enhancer; - } - } - - protected static class CleanupThread extends Thread { - @Override - public void run() { - synchronized (CleanupThread.class) { - for (LegacyComponentLocator locator : s_locators.values()) { - Iterator> itAdapters = locator._adapterMap.values().iterator(); - while (itAdapters.hasNext()) { - Adapters adapters = itAdapters.next(); - itAdapters.remove(); - for (ComponentInfo adapter : adapters._infos) { - if (adapter.singleton) { - Singleton singleton = s_singletons.get(adapter.clazz); - if (singleton.state == Singleton.State.Started) { - s_logger.info("Asking " + adapter.getName() + " to shutdown."); - adapter.instance.stop(); - singleton.state = Singleton.State.Stopped; - } else { - s_logger.debug("Skippng " + adapter.getName() + " because it has already stopped"); - } - } else { - s_logger.info("Asking " + adapter.getName() + " to shutdown."); - adapter.instance.stop(); - } - } - } - } - - for (LegacyComponentLocator locator : s_locators.values()) { - Iterator> itManagers = locator._managerMap.values().iterator(); - while (itManagers.hasNext()) { - ComponentInfo manager = itManagers.next(); - itManagers.remove(); - if (manager.singleton == true) { - Singleton singleton = s_singletons.get(manager.clazz); - if (singleton != null && singleton.state == Singleton.State.Started) { - s_logger.info("Asking Manager " + manager.getName() + " to shutdown."); - manager.instance.stop(); - singleton.state = Singleton.State.Stopped; - } else { - s_logger.info("Skipping Manager " + manager.getName() + " because it is not in a state to shutdown."); - } - } - } - } - } - } - } - - static class Singleton { - public enum State { - Instantiated, - Injected, - Configured, - Started, - Stopped - } - - public Object singleton; - public State state; - - public Singleton(Object singleton) { - this.singleton = singleton; - this.state = State.Instantiated; - } - } - - protected class InterceptorDispatcher implements MethodInterceptor { - - @Override - public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { - ArrayList, Object>> interceptors = new ArrayList, Object>>(); - for (AnnotationInterceptor interceptor : s_interceptors) { - if (interceptor.needToIntercept(method)) { - Object obj = interceptor.interceptStart(method); - interceptors.add(new Pair, Object>((AnnotationInterceptor)interceptor, obj)); - } - } - boolean success = false; - try { - Object obj = methodProxy.invokeSuper(object, args); - success = true; - return obj; - } finally { - for (Pair, Object> interceptor : interceptors) { - if (success) { - interceptor.first().interceptComplete(method, interceptor.second()); - } else { - interceptor.first().interceptException(method, interceptor.second()); - } - } - } - } - } - - protected static class InterceptorFilter implements CallbackFilter { - @Override - public int accept(Method method) { - int index = 0; - for (int i = 2; i < s_callbacks.length; i++) { - AnnotationInterceptor interceptor = (AnnotationInterceptor)s_callbacks[i]; - if (interceptor.needToIntercept(method)) { - if (index == 0) { - index = i; - } else { - return 1; - } - } - } - - return index; - } - } -} diff --git a/utils/src/com/cloud/utils/component/Plugin.java b/utils/src/com/cloud/utils/component/Plugin.java deleted file mode 100755 index ffd704c7558..00000000000 --- a/utils/src/com/cloud/utils/component/Plugin.java +++ /dev/null @@ -1,64 +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 -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package com.cloud.utils.component; - -import java.util.List; - -import com.cloud.utils.Pair; - - -/** - * CloudStack uses Adapters to implement different capabilities. - * There are different Adapters such as NetworkGuru, NetworkElement, - * HypervisorGuru, DeploymentPlanner, etc. However, Adapters only - * defines what CloudStack needs from the implementation. What about - * what the Adapter itself needs, such as configurations and administrative - * operations, and what if one implementation can - * implement two different Adapters? - * - * Plugin is a CloudStack container for Adapters. It rolls the following - * capabilities into the one package for CloudStack to load at runtime. - * - REST API commands supported by the Plugin. - * - Components needed by the Plugin. - * - Adapters implemented by the Plugin. - * - Database operations - * - */ -public interface Plugin extends PluggableService { - - /** - * Retrieves the component libraries needed by this Plugin. - * ComponentLocator put these components and add them to the startup - * and shutdown processes of CloudStack. This is only needed if the - * Plugin uses ComponentLocator to inject what it needs. If the - * Plugin uses other mechanisms, then it can return null here. - * - * @return a component library that contains the components this Plugin - * contains and needs. - */ - ComponentLibrary getComponentLibrary(); - - /** - * Retrieves the list of Adapters and the interface they implement. It - * can be an empty list if the Plugin does not implement any. - * - * @return list of pairs where the first is the interface and the second - * is the adapter. - */ - List, Class>> getAdapterImplementations(); -} diff --git a/utils/test/com/cloud/utils/component/MockComponentLocator.java b/utils/test/com/cloud/utils/component/MockComponentLocator.java deleted file mode 100755 index d95d2629d06..00000000000 --- a/utils/test/com/cloud/utils/component/MockComponentLocator.java +++ /dev/null @@ -1,121 +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 -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.utils.component; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import net.sf.cglib.proxy.Callback; -import net.sf.cglib.proxy.NoOp; - -import com.cloud.utils.Pair; -import com.cloud.utils.db.DatabaseCallback; -import com.cloud.utils.db.DatabaseCallbackFilter; -import com.cloud.utils.db.GenericDao; - -/** - * defining mock components. - */ -public class MockComponentLocator extends LegacyComponentLocator { - MockComponentLibrary _library = new MockComponentLibrary(); - - public MockComponentLocator(String server) { - super(server); - } - - public ComponentInfo> addDao(String name, Class> dao) { - return _library.addDao(name, dao); - } - - public ComponentInfo addManager(String name, Class manager) { - return _library.addManager(name, manager); - } - - public ComponentInfo addOneAdapter(Class interphace, String name, Class adapterClass) { - return _library.addOneAdapter(interphace, name, adapterClass); - } - - public List> addAdapterChain(Class interphace, List>> adapters) { - return _library.addAdapterChain(interphace, adapters); - } - - public ComponentInfo addService(String name, Class serviceInterphace, Class service) { - return _library.addService(name, serviceInterphace, service); - } - - @Override - protected Pair>>> parse2(String filename) { - Pair>>> result = new Pair>>>(new XmlHandler("fake"), new HashMap>>()); - _daoMap = new LinkedHashMap>>(); - _managerMap = new LinkedHashMap>(); - _checkerMap = new LinkedHashMap>(); - _adapterMap = new HashMap>(); - _pluginsMap = new HashMap>(); - _factories = new HashMap, Class>(); - _daoMap.putAll(_library.getDaos()); - _managerMap.putAll(_library.getManagers()); - result.second().putAll(_library.getAdapters()); - _factories.putAll(_library.getFactories()); - _pluginsMap.putAll(_library.getPluggableServices()); - return result; - } - - public void makeActive(InterceptorLibrary interceptors) { - s_singletons.clear(); - s_locators.clear(); - s_factories.clear(); - s_callbacks = new Callback[] { NoOp.INSTANCE, new DatabaseCallback()}; - s_callbackFilter = new DatabaseCallbackFilter(); - s_interceptors.clear(); - if (interceptors != null) { - resetInterceptors(interceptors); - } - s_tl.set(this); - parse("fake file"); - } - - protected class MockComponentLibrary extends ComponentLibraryBase implements ComponentLibrary { - - @Override - public Map>> getAdapters() { - return _adapters; - } - - @Override - public Map, Class> getFactories() { - return new HashMap, Class>(); - } - - @Override - public Map>> getDaos() { - return _daos; - } - - @Override - public Map> getManagers() { - return _managers; - } - - @Override - public Map> getPluggableServices() { - return _pluggableServices; - } - } -} diff --git a/utils/test/com/cloud/utils/testcase/ComponentSetup.java b/utils/test/com/cloud/utils/testcase/ComponentSetup.java deleted file mode 100644 index ba9b9e74adf..00000000000 --- a/utils/test/com/cloud/utils/testcase/ComponentSetup.java +++ /dev/null @@ -1,28 +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 -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.utils.testcase; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Retention(RetentionPolicy.RUNTIME) -public @interface ComponentSetup { - String managerName(); - String setupXml(); - String log4j() default "log4j-cloud"; -} - diff --git a/utils/test/com/cloud/utils/testcase/ComponentTestCase.java b/utils/test/com/cloud/utils/testcase/ComponentTestCase.java deleted file mode 100644 index 6fe7af10200..00000000000 --- a/utils/test/com/cloud/utils/testcase/ComponentTestCase.java +++ /dev/null @@ -1,44 +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 -// the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package com.cloud.utils.testcase; - -import java.lang.annotation.Annotation; - -import com.cloud.utils.component.LegacyComponentLocator; - -public class ComponentTestCase extends Log4jEnabledTestCase { - @Override - protected void setUp() { - super.setUp(); - - Annotation[] annotations = getClass().getAnnotations(); - if(annotations != null) { - for(Annotation annotation : annotations) { - if(annotation instanceof ComponentSetup) { - LegacyComponentLocator.getLocator( - ((ComponentSetup)annotation).managerName(), - ((ComponentSetup)annotation).setupXml(), - ((ComponentSetup)annotation).log4j() - ); - - break; - } - } - } - } -} - From 56e5fbdee29af94b7c3ffeebe1e757292b9f09ae Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Thu, 10 Jan 2013 11:44:47 -0800 Subject: [PATCH 18/73] removed import of componentlocator and inject from all files --- .../org/apache/cloudstack/api/BaseCmd.java | 2 +- .../storage/template/UploadManagerImpl.java | 551 ++++++------ .../ObjectInDataStoreManagerImpl.java | 2 +- .../storage/volume/db/VolumeDao2Impl.java | 2 +- .../api/commands/netapp/AssociateLunCmd.java | 2 +- .../api/commands/netapp/CreateLunCmd.java | 2 +- .../netapp/CreateVolumeOnFilerCmd.java | 2 +- .../commands/netapp/CreateVolumePoolCmd.java | 2 +- .../commands/netapp/DeleteVolumePoolCmd.java | 2 +- .../api/commands/netapp/DestroyLunCmd.java | 2 +- .../netapp/DestroyVolumeOnFilerCmd.java | 2 +- .../api/commands/netapp/DissociateLunCmd.java | 2 +- .../api/commands/netapp/ListLunsCmd.java | 2 +- .../commands/netapp/ListVolumePoolsCmd.java | 2 +- .../netapp/ListVolumesOnFilerCmd.java | 2 +- .../commands/netapp/ModifyVolumePoolCmd.java | 2 +- .../allocator/impl/RandomAllocator.java | 43 +- .../resource/LibvirtComputingResource.java | 2 +- .../agent/manager/MockAgentManagerImpl.java | 2 +- .../agent/manager/MockStorageManagerImpl.java | 2 +- .../agent/manager/MockVmManagerImpl.java | 2 +- .../agent/manager/SimulatorManagerImpl.java | 2 +- .../api/commands/ConfigureSimulator.java | 2 +- .../com/cloud/resource/AgentResourceBase.java | 2 +- .../cloud/resource/SimulatorDiscoverer.java | 2 +- .../SimulatorSecondaryDiscoverer.java | 2 +- .../com/cloud/simulator/SimulatorGuru.java | 2 +- .../cloud/simulator/dao/MockVMDaoImpl.java | 2 +- .../vmware/VmwareServerDiscoverer.java | 2 +- .../vmware/manager/VmwareManagerImpl.java | 2 +- .../vmware/resource/VmwareContextFactory.java | 2 +- .../vmware/resource/VmwareResource.java | 2 +- .../network/lb/dao/ElasticLbVmMapDaoImpl.java | 2 +- .../server/auth/MD5UserAuthenticator.java | 2 +- .../auth/PlainTextUserAuthenticator.java | 2 +- .../auth/SHA256SaltedUserAuthenticator.java | 4 +- .../cloud/agent/manager/AgentManagerImpl.java | 5 - .../manager/ClusteredAgentManagerImpl.java | 3 - server/src/com/cloud/api/ApiServer.java | 24 +- server/src/com/cloud/api/ApiServlet.java | 28 +- .../api/commands/AddTrafficMonitorCmd.java | 128 +-- .../api/commands/DeleteTrafficMonitorCmd.java | 93 +-- .../api/commands/ListTrafficMonitorsCmd.java | 2 +- .../query/dao/DomainRouterJoinDaoImpl.java | 14 +- .../cloud/api/query/dao/HostJoinDaoImpl.java | 30 +- .../api/query/dao/ProjectJoinDaoImpl.java | 12 +- .../api/query/dao/ResourceTagJoinDaoImpl.java | 12 +- .../query/dao/SecurityGroupJoinDaoImpl.java | 14 +- .../api/query/dao/StoragePoolJoinDaoImpl.java | 14 +- .../api/query/dao/UserVmJoinDaoImpl.java | 20 +- .../api/query/dao/VolumeJoinDaoImpl.java | 24 +- .../async/AsyncJobExecutorContextImpl.java | 155 ++-- .../com/cloud/async/AsyncJobManagerImpl.java | 784 +++++++++--------- .../com/cloud/async/SyncQueueManagerImpl.java | 351 ++++---- .../cloud/capacity/dao/CapacityDaoImpl.java | 560 +++++++------ .../com/cloud/cluster/CheckPointManager.java | 52 -- .../cloud/cluster/CheckPointManagerImpl.java | 247 ------ server/src/com/cloud/cluster/CleanupMaid.java | 41 - .../com/cloud/cluster/ClusterManagerImpl.java | 405 ++++----- server/src/com/cloud/cluster/StackMaid.java | 153 ---- .../dao/ResourceCountDaoImpl.java | 255 +++--- .../AgentBasedConsoleProxyManager.java | 33 +- .../ConsoleProxyBalanceAllocator.java | 77 +- .../consoleproxy/ConsoleProxyManagerImpl.java | 133 ++- .../StaticConsoleProxyManager.java | 17 +- .../src/com/cloud/dc/dao/ClusterDaoImpl.java | 111 ++- .../src/com/cloud/dc/dao/HostPodDaoImpl.java | 118 +-- server/src/com/cloud/dc/dao/VlanDaoImpl.java | 377 +++++---- .../src/com/cloud/deploy/FirstFitPlanner.java | 148 ++-- .../cloud/ha/HighAvailabilityManagerImpl.java | 76 +- .../src/com/cloud/host/dao/HostDaoImpl.java | 283 ++++--- .../hypervisor/HypervisorGuruManagerImpl.java | 90 +- .../com/cloud/maint/UpgradeManagerImpl.java | 29 +- .../migration/Db21to22MigrationUtil.java | 162 ++-- .../network/ExteralIpAddressAllocator.java | 236 +++--- .../ExternalNetworkDeviceManagerImpl.java | 44 +- .../network/as/AutoScaleManagerImpl.java | 19 +- .../network/dao/FirewallRulesDaoImpl.java | 6 +- .../cloud/network/dao/IPAddressDaoImpl.java | 58 +- .../network/dao/LoadBalancerDaoImpl.java | 3 +- .../com/cloud/network/dao/NetworkDaoImpl.java | 1 - .../network/dao/PhysicalNetworkDaoImpl.java | 9 +- .../dao/Site2SiteVpnConnectionDaoImpl.java | 12 +- .../dao/Site2SiteVpnGatewayDaoImpl.java | 7 +- .../network/guru/ControlNetworkGuru.java | 2 +- .../VirtualNetworkApplianceManagerImpl.java | 2 +- .../rules/dao/PortForwardingRulesDaoImpl.java | 2 +- .../security/SecurityGroupManagerImpl.java | 2 +- .../security/dao/SecurityGroupDaoImpl.java | 2 +- .../com/cloud/network/vpc/VpcManagerImpl.java | 2 +- .../network/vpc/dao/StaticRouteDaoImpl.java | 2 +- .../com/cloud/network/vpc/dao/VpcDaoImpl.java | 2 +- .../vpn/RemoteAccessVpnManagerImpl.java | 2 +- .../network/vpn/Site2SiteVpnManagerImpl.java | 2 +- .../cloud/projects/dao/ProjectDaoImpl.java | 2 +- .../com/cloud/resource/DiscovererBase.java | 2 +- .../cloud/servlet/ConsoleProxyServlet.java | 2 +- .../servlet/RegisterCompleteServlet.java | 2 +- .../com/cloud/storage/StorageManagerImpl.java | 2 +- .../storage/StorageMigrationCleanupMaid.java | 2 +- ...GarbageCollectingStoragePoolAllocator.java | 2 +- .../allocator/UseLocalForRootAllocator.java | 2 +- .../cloud/storage/dao/SnapshotDaoImpl.java | 2 +- .../cloud/storage/dao/StoragePoolDaoImpl.java | 2 +- .../cloud/storage/dao/VMTemplateDaoImpl.java | 2 +- .../com/cloud/storage/dao/VolumeDaoImpl.java | 2 +- .../storage/listener/StoragePoolMonitor.java | 2 +- .../DummySecondaryStorageResource.java | 2 +- .../com/cloud/storage/s3/S3ManagerImpl.java | 2 +- .../storage/snapshot/SnapshotManagerImpl.java | 2 +- .../snapshot/SnapshotSchedulerImpl.java | 2 +- .../cloud/template/TemplateManagerImpl.java | 2 +- server/src/com/cloud/test/IPRangeConfig.java | 2 +- server/src/com/cloud/test/PodZoneConfig.java | 2 +- .../com/cloud/upgrade/DatabaseCreator.java | 2 +- .../upgrade/DatabaseIntegrityChecker.java | 2 +- .../cloud/upgrade/DatabaseUpgradeChecker.java | 2 +- .../PremiumDatabaseUpgradeChecker.java | 2 +- .../com/cloud/user/AccountManagerImpl.java | 2 +- .../src/com/cloud/vm/UserVmManagerImpl.java | 2 +- .../src/com/cloud/vm/dao/UserVmDaoImpl.java | 2 +- .../com/cloud/vm/dao/VMInstanceDaoImpl.java | 2 +- .../SecurityGroupManagerImpl2Test.java | 2 +- .../com/cloud/snapshot/SnapshotDaoTest.java | 2 +- .../cloud/storage/dao/StoragePoolDaoTest.java | 2 +- .../AdvanceZone217To224UpgradeTest.java | 2 +- .../AdvanceZone223To224UpgradeTest.java | 2 +- .../upgrade/BasicZone218To224UpgradeTest.java | 2 +- .../upgrade/HostCapacity218to22Test.java | 2 +- .../InstanceGroup218To224UpgradeTest.java | 2 +- .../PortForwarding218To224UpgradeTest.java | 2 +- .../upgrade/Sanity220To224UpgradeTest.java | 2 +- .../upgrade/Sanity222To224UpgradeTest.java | 2 +- .../upgrade/Sanity223To225UpgradeTest.java | 2 +- .../upgrade/Sanity224To225UpgradeTest.java | 2 +- .../upgrade/Template2214To30UpgradeTest.java | 2 +- .../cloud/upgrade/Test2214To30DBUpgrade.java | 2 +- .../upgrade/Usage217To224UpgradeTest.java | 2 +- .../UsageEvents218To224UpgradeTest.java | 2 +- .../com/cloud/vm/dao/UserVmDaoImplTest.java | 2 +- .../com/cloud/vpc/MockVpcManagerImpl.java | 2 +- server/test/com/cloud/vpc/VpcApiUnitTest.java | 2 +- .../cloud/usage/UsageAlertManagerImpl.java | 2 +- .../src/com/cloud/usage/UsageManagerImpl.java | 4 +- usage/src/com/cloud/usage/UsageServer.java | 2 +- .../usage/parser/IPAddressUsageParser.java | 2 +- .../usage/parser/LoadBalancerUsageParser.java | 2 +- .../parser/NetworkOfferingUsageParser.java | 2 +- .../usage/parser/NetworkUsageParser.java | 2 +- .../parser/PortForwardingUsageParser.java | 2 +- .../parser/SecurityGroupUsageParser.java | 2 +- .../usage/parser/StorageUsageParser.java | 2 +- .../usage/parser/VMInstanceUsageParser.java | 2 +- .../usage/parser/VPNUserUsageParser.java | 2 +- .../cloud/usage/parser/VolumeUsageParser.java | 2 +- .../com/cloud/utils/db/TransactionTest.java | 2 +- .../utils/log/CglibThrowableRendererTest.java | 2 +- 157 files changed, 2856 insertions(+), 3431 deletions(-) delete mode 100644 server/src/com/cloud/cluster/CheckPointManager.java delete mode 100644 server/src/com/cloud/cluster/CheckPointManagerImpl.java delete mode 100644 server/src/com/cloud/cluster/CleanupMaid.java delete mode 100644 server/src/com/cloud/cluster/StackMaid.java diff --git a/api/src/org/apache/cloudstack/api/BaseCmd.java b/api/src/org/apache/cloudstack/api/BaseCmd.java index c0ef8a0985e..ba2c4d21a50 100644 --- a/api/src/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseCmd.java @@ -141,7 +141,7 @@ public abstract class BaseCmd { public static QueryService _queryService; - static void setComponents(ResponseGenerator generator) { + public static void setComponents(ResponseGenerator generator) { _mgr = ComponentContext.getComponent(ManagementService.class); _accountService = ComponentContext.getComponent(AccountService.class); _configService = ComponentContext.getComponent(ConfigurationService.class); diff --git a/core/src/com/cloud/storage/template/UploadManagerImpl.java b/core/src/com/cloud/storage/template/UploadManagerImpl.java index 2dd1751aeaa..0de1c6ccf67 100755 --- a/core/src/com/cloud/storage/template/UploadManagerImpl.java +++ b/core/src/com/cloud/storage/template/UploadManagerImpl.java @@ -21,6 +21,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -46,15 +47,13 @@ import com.cloud.storage.resource.SecondaryStorageResource; import com.cloud.storage.template.TemplateUploader.Status; import com.cloud.storage.template.TemplateUploader.UploadCompleteCallback; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.component.Adapters; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; public class UploadManagerImpl implements UploadManager { - public class Completion implements UploadCompleteCallback { + public class Completion implements UploadCompleteCallback { private final String jobId; public Completion(String jobId) { @@ -66,180 +65,180 @@ public class UploadManagerImpl implements UploadManager { setUploadStatus(jobId, status); } } - - private static class UploadJob { - private final TemplateUploader tu; - private final String jobId; - private final String name; - private final ImageFormat format; - private String tmpltPath; - private String description; - private String checksum; - private Long accountId; - private String installPathPrefix; - private long templatesize; - private long id; - public UploadJob(TemplateUploader tu, String jobId, long id, String name, ImageFormat format, boolean hvm, Long accountId, String descr, String cksum, String installPathPrefix) { - super(); - this.tu = tu; - this.jobId = jobId; - this.name = name; - this.format = format; - this.accountId = accountId; - this.description = descr; - this.checksum = cksum; - this.installPathPrefix = installPathPrefix; - this.templatesize = 0; - this.id = id; - } + private static class UploadJob { + private final TemplateUploader tu; + private final String jobId; + private final String name; + private final ImageFormat format; + private String tmpltPath; + private String description; + private String checksum; + private Long accountId; + private String installPathPrefix; + private long templatesize; + private long id; - public TemplateUploader getTd() { - return tu; - } + public UploadJob(TemplateUploader tu, String jobId, long id, String name, ImageFormat format, boolean hvm, Long accountId, String descr, String cksum, String installPathPrefix) { + super(); + this.tu = tu; + this.jobId = jobId; + this.name = name; + this.format = format; + this.accountId = accountId; + this.description = descr; + this.checksum = cksum; + this.installPathPrefix = installPathPrefix; + this.templatesize = 0; + this.id = id; + } - public String getDescription() { - return description; - } + public TemplateUploader getTd() { + return tu; + } - public String getChecksum() { - return checksum; - } + public String getDescription() { + return description; + } - public UploadJob(TemplateUploader td, String jobId, UploadCommand cmd) { - this.tu = td; - this.jobId = jobId; - this.name = cmd.getName(); - this.format = cmd.getFormat(); - } + public String getChecksum() { + return checksum; + } - public TemplateUploader getTemplateUploader() { - return tu; - } + public UploadJob(TemplateUploader td, String jobId, UploadCommand cmd) { + this.tu = td; + this.jobId = jobId; + this.name = cmd.getName(); + this.format = cmd.getFormat(); + } - public String getJobId() { - return jobId; - } + public TemplateUploader getTemplateUploader() { + return tu; + } - public String getTmpltName() { - return name; - } + public String getJobId() { + return jobId; + } - public ImageFormat getFormat() { - return format; - } + public String getTmpltName() { + return name; + } - public Long getAccountId() { - return accountId; - } + public ImageFormat getFormat() { + return format; + } - public long getId() { - return id; - } + public Long getAccountId() { + return accountId; + } - public void setTmpltPath(String tmpltPath) { - this.tmpltPath = tmpltPath; - } + public long getId() { + return id; + } - public String getTmpltPath() { - return tmpltPath; - } + public void setTmpltPath(String tmpltPath) { + this.tmpltPath = tmpltPath; + } - public String getInstallPathPrefix() { - return installPathPrefix; - } + public String getTmpltPath() { + return tmpltPath; + } - public void cleanup() { - if (tu != null) { - String upldPath = tu.getUploadLocalPath(); - if (upldPath != null) { - File f = new File(upldPath); - f.delete(); - } - } - } + public String getInstallPathPrefix() { + return installPathPrefix; + } - public void setTemplatesize(long templatesize) { - this.templatesize = templatesize; - } + public void cleanup() { + if (tu != null) { + String upldPath = tu.getUploadLocalPath(); + if (upldPath != null) { + File f = new File(upldPath); + f.delete(); + } + } + } + + public void setTemplatesize(long templatesize) { + this.templatesize = templatesize; + } + + public long getTemplatesize() { + return templatesize; + } + } + public static final Logger s_logger = Logger.getLogger(UploadManagerImpl.class); + private ExecutorService threadPool; + private final Map jobs = new ConcurrentHashMap(); + private String parentDir; + private List _processors; + private String publicTemplateRepo; + private final String extractMountPoint = "/mnt/SecStorage/extractmnt"; + private StorageLayer _storage; + private int installTimeoutPerGig; + private boolean _sslCopy; + private String _name; + private boolean hvm; + + + @Override + public String uploadPublicTemplate(long id, String url, String name, + ImageFormat format, Long accountId, String descr, + String cksum, String installPathPrefix, String userName, + String passwd, long templateSizeInBytes) { - public long getTemplatesize() { - return templatesize; - } - } - public static final Logger s_logger = Logger.getLogger(UploadManagerImpl.class); - private ExecutorService threadPool; - private final Map jobs = new ConcurrentHashMap(); - private String parentDir; - private Adapters _processors; - private String publicTemplateRepo; - private String extractMountPoint = "/mnt/SecStorage/extractmnt"; - private StorageLayer _storage; - private int installTimeoutPerGig; - private boolean _sslCopy; - private String _name; - private boolean hvm; - - - @Override - public String uploadPublicTemplate(long id, String url, String name, - ImageFormat format, Long accountId, String descr, - String cksum, String installPathPrefix, String userName, - String passwd, long templateSizeInBytes) { - UUID uuid = UUID.randomUUID(); String jobId = uuid.toString(); String completePath = parentDir + File.separator + installPathPrefix; s_logger.debug("Starting upload from " + completePath); - + URI uri; - try { - uri = new URI(url); - } catch (URISyntaxException e) { - s_logger.error("URI is incorrect: " + url); - throw new CloudRuntimeException("URI is incorrect: " + url); - } - TemplateUploader tu; - if ((uri != null) && (uri.getScheme() != null)) { - if (uri.getScheme().equalsIgnoreCase("ftp")) { - tu = new FtpTemplateUploader(completePath, url, new Completion(jobId), templateSizeInBytes); - } else { - s_logger.error("Scheme is not supported " + url); - throw new CloudRuntimeException("Scheme is not supported " + url); - } - } else { - s_logger.error("Unable to download from URL: " + url); - throw new CloudRuntimeException("Unable to download from URL: " + url); - } - UploadJob uj = new UploadJob(tu, jobId, id, name, format, hvm, accountId, descr, cksum, installPathPrefix); - jobs.put(jobId, uj); - threadPool.execute(tu); + try { + uri = new URI(url); + } catch (URISyntaxException e) { + s_logger.error("URI is incorrect: " + url); + throw new CloudRuntimeException("URI is incorrect: " + url); + } + TemplateUploader tu; + if ((uri != null) && (uri.getScheme() != null)) { + if (uri.getScheme().equalsIgnoreCase("ftp")) { + tu = new FtpTemplateUploader(completePath, url, new Completion(jobId), templateSizeInBytes); + } else { + s_logger.error("Scheme is not supported " + url); + throw new CloudRuntimeException("Scheme is not supported " + url); + } + } else { + s_logger.error("Unable to download from URL: " + url); + throw new CloudRuntimeException("Unable to download from URL: " + url); + } + UploadJob uj = new UploadJob(tu, jobId, id, name, format, hvm, accountId, descr, cksum, installPathPrefix); + jobs.put(jobId, uj); + threadPool.execute(tu); - return jobId; - - } + return jobId; - @Override - public String getUploadError(String jobId) { + } + + @Override + public String getUploadError(String jobId) { UploadJob uj = jobs.get(jobId); if (uj != null) { return uj.getTemplateUploader().getUploadError(); } return null; - } + } - @Override - public int getUploadPct(String jobId) { - UploadJob uj = jobs.get(jobId); + @Override + public int getUploadPct(String jobId) { + UploadJob uj = jobs.get(jobId); if (uj != null) { return uj.getTemplateUploader().getUploadPercent(); } return 0; - } + } - @Override - public Status getUploadStatus(String jobId) { + @Override + public Status getUploadStatus(String jobId) { UploadJob job = jobs.get(jobId); if (job != null) { TemplateUploader tu = job.getTemplateUploader(); @@ -248,8 +247,8 @@ public class UploadManagerImpl implements UploadManager { } } return Status.UNKNOWN; - } - + } + public static UploadVO.Status convertStatus(Status tds) { switch (tds) { case ABORTED: @@ -277,11 +276,11 @@ public class UploadManagerImpl implements UploadManager { public com.cloud.storage.UploadVO.Status getUploadStatus2(String jobId) { return convertStatus(getUploadStatus(jobId)); } - @Override - public String getPublicTemplateRepo() { - // TODO Auto-generated method stub - return null; - } + @Override + public String getPublicTemplateRepo() { + // TODO Auto-generated method stub + return null; + } private UploadAnswer handleUploadProgressCmd(UploadProgressCommand cmd) { String jobId = cmd.getJobId(); @@ -290,7 +289,7 @@ public class UploadManagerImpl implements UploadManager { if (jobId != null) uj = jobs.get(jobId); if (uj == null) { - return new UploadAnswer(null, 0, "Cannot find job", com.cloud.storage.UploadVO.Status.UNKNOWN, "", "", 0); + return new UploadAnswer(null, 0, "Cannot find job", com.cloud.storage.UploadVO.Status.UNKNOWN, "", "", 0); } TemplateUploader td = uj.getTemplateUploader(); switch (cmd.getRequest()) { @@ -300,7 +299,7 @@ public class UploadManagerImpl implements UploadManager { td.stopUpload(); sleep(); break; - /*case RESTART: + /*case RESTART: td.stopUpload(); sleep(); threadPool.execute(td); @@ -316,10 +315,10 @@ public class UploadManagerImpl implements UploadManager { return new UploadAnswer(jobId, getUploadPct(jobId), getUploadError(jobId), getUploadStatus2(jobId), getUploadLocalPath(jobId), getInstallPath(jobId), getUploadTemplateSize(jobId)); } - + @Override public UploadAnswer handleUploadCommand(SecondaryStorageResource resource, UploadCommand cmd) { - s_logger.warn("Handling the upload " +cmd.getInstallPath() + " " + cmd.getId()); + s_logger.warn("Handling the upload " +cmd.getInstallPath() + " " + cmd.getId()); if (cmd instanceof UploadProgressCommand) { return handleUploadProgressCmd((UploadProgressCommand) cmd); } @@ -327,9 +326,9 @@ public class UploadManagerImpl implements UploadManager { String user = null; String password = null; String jobId = uploadPublicTemplate(cmd.getId(), cmd.getUrl(), cmd.getName(), - cmd.getFormat(), cmd.getAccountId(), cmd.getDescription(), - cmd.getChecksum(), cmd.getInstallPath(), user, password, - cmd.getTemplateSizeInBytes()); + cmd.getFormat(), cmd.getAccountId(), cmd.getDescription(), + cmd.getChecksum(), cmd.getInstallPath(), user, password, + cmd.getTemplateSizeInBytes()); sleep(); if (jobId == null) { return new UploadAnswer(null, 0, "Internal Error", com.cloud.storage.UploadVO.Status.UPLOAD_ERROR, "", "", 0); @@ -337,18 +336,18 @@ public class UploadManagerImpl implements UploadManager { return new UploadAnswer(jobId, getUploadPct(jobId), getUploadError(jobId), getUploadStatus2(jobId), getUploadLocalPath(jobId), getInstallPath(jobId), getUploadTemplateSize(jobId)); } - + @Override public CreateEntityDownloadURLAnswer handleCreateEntityURLCommand(CreateEntityDownloadURLCommand cmd){ - - boolean isApacheUp = checkAndStartApache(); - if (!isApacheUp){ - String errorString = "Error in starting Apache server "; + + boolean isApacheUp = checkAndStartApache(); + if (!isApacheUp){ + String errorString = "Error in starting Apache server "; s_logger.error(errorString); return new CreateEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE); - } + } // Create the directory structure so that its visible under apache server root - String extractDir = "/var/www/html/userdata/"; + String extractDir = "/var/www/html/userdata/"; Script command = new Script("mkdir", s_logger); command.add("-p"); command.add(extractDir); @@ -358,19 +357,19 @@ public class UploadManagerImpl implements UploadManager { s_logger.error(errorString); return new CreateEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE); } - + // Create a random file under the directory for security reasons. String uuid = cmd.getExtractLinkUUID(); - command = new Script("touch", s_logger); - command.add(extractDir + uuid); - result = command.execute(); - if (result != null) { - String errorString = "Error in creating file " +uuid+ " ,error: " + result; - s_logger.warn(errorString); - return new CreateEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE); - } + command = new Script("touch", s_logger); + command.add(extractDir + uuid); + result = command.execute(); + if (result != null) { + String errorString = "Error in creating file " +uuid+ " ,error: " + result; + s_logger.warn(errorString); + return new CreateEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE); + } + - // Create a symbolic link from the actual directory to the template location. The entity would be directly visible under /var/www/html/userdata/cmd.getInstallPath(); command = new Script("/bin/bash", s_logger); command.add("-c"); @@ -381,11 +380,11 @@ public class UploadManagerImpl implements UploadManager { s_logger.error(errorString); return new CreateEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE); } - + return new CreateEntityDownloadURLAnswer("", CreateEntityDownloadURLAnswer.RESULT_SUCCESS); - + } - + @Override public DeleteEntityDownloadURLAnswer handleDeleteEntityDownloadURLCommand(DeleteEntityDownloadURLCommand cmd){ @@ -394,8 +393,8 @@ public class UploadManagerImpl implements UploadManager { String path = cmd.getPath(); Script command = new Script("/bin/bash", s_logger); command.add("-c"); - - //We just need to remove the UUID.vhd + + //We just need to remove the UUID.vhd String extractUrl = cmd.getExtractUrl(); command.add("unlink /var/www/html/userdata/" +extractUrl.substring(extractUrl.lastIndexOf(File.separator) + 1)); String result = command.execute(); @@ -404,7 +403,7 @@ public class UploadManagerImpl implements UploadManager { s_logger.warn(errorString); return new DeleteEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE); } - + // If its a volume also delete the Hard link since it was created only for the purpose of download. if(cmd.getType() == Upload.Type.VOLUME){ command = new Script("/bin/bash", s_logger); @@ -418,31 +417,31 @@ public class UploadManagerImpl implements UploadManager { return new DeleteEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE); } } - + return new DeleteEntityDownloadURLAnswer("", CreateEntityDownloadURLAnswer.RESULT_SUCCESS); } - private String getInstallPath(String jobId) { - // TODO Auto-generated method stub - return null; - } + private String getInstallPath(String jobId) { + // TODO Auto-generated method stub + return null; + } - private String getUploadLocalPath(String jobId) { - // TODO Auto-generated method stub - return null; - } + private String getUploadLocalPath(String jobId) { + // TODO Auto-generated method stub + return null; + } - private long getUploadTemplateSize(String jobId){ - UploadJob uj = jobs.get(jobId); + private long getUploadTemplateSize(String jobId){ + UploadJob uj = jobs.get(jobId); if (uj != null) { return uj.getTemplatesize(); } return 0; - } + } - @Override - public boolean configure(String name, Map params) - throws ConfigurationException { + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { _name = name; String value = null; @@ -457,21 +456,25 @@ public class UploadManagerImpl implements UploadManager { Class clazz; try { clazz = (Class) Class.forName(value); + _storage = clazz.newInstance(); } catch (ClassNotFoundException e) { throw new ConfigurationException("Unable to instantiate " + value); + } catch (InstantiationException e) { + throw new ConfigurationException("Unable to instantiate " + value); + } catch (IllegalAccessException e) { + throw new ConfigurationException("Unable to instantiate " + value); } - _storage = ComponentLocator.inject(clazz); } String useSsl = (String)params.get("sslcopy"); if (useSsl != null) { - _sslCopy = Boolean.parseBoolean(useSsl); - + _sslCopy = Boolean.parseBoolean(useSsl); + } String inSystemVM = (String)params.get("secondary.storage.vm"); if (inSystemVM != null && "true".equalsIgnoreCase(inSystemVM)) { - s_logger.info("UploadManager: starting additional services since we are inside system vm"); - startAdditionalServices(); - //blockOutgoingOnPrivate(); + s_logger.info("UploadManager: starting additional services since we are inside system vm"); + startAdditionalServices(); + //blockOutgoingOnPrivate(); } value = (String) params.get("install.timeout.pergig"); @@ -489,53 +492,53 @@ public class UploadManagerImpl implements UploadManager { threadPool = Executors.newFixedThreadPool(numInstallThreads); return true; - } - - private void startAdditionalServices() { - - - Script command = new Script("rm", s_logger); - command.add("-rf"); - command.add(extractMountPoint); - String result = command.execute(); - if (result != null) { - s_logger.warn("Error in creating file " +extractMountPoint+ " ,error: " + result ); - return; - } - - command = new Script("touch", s_logger); - command.add(extractMountPoint); - result = command.execute(); - if (result != null) { - s_logger.warn("Error in creating file " +extractMountPoint+ " ,error: " + result ); - return; - } - - command = new Script("/bin/bash", s_logger); - command.add("-c"); - command.add("ln -sf " + parentDir + " " +extractMountPoint); - result = command.execute(); - if (result != null) { - s_logger.warn("Error in linking err=" + result ); - return; - } - - } + } - @Override - public String getName() { - return _name; - } + private void startAdditionalServices() { - @Override - public boolean start() { - return true; - } - @Override - public boolean stop() { - return true; - } + Script command = new Script("rm", s_logger); + command.add("-rf"); + command.add(extractMountPoint); + String result = command.execute(); + if (result != null) { + s_logger.warn("Error in creating file " +extractMountPoint+ " ,error: " + result ); + return; + } + + command = new Script("touch", s_logger); + command.add(extractMountPoint); + result = command.execute(); + if (result != null) { + s_logger.warn("Error in creating file " +extractMountPoint+ " ,error: " + result ); + return; + } + + command = new Script("/bin/bash", s_logger); + command.add("-c"); + command.add("ln -sf " + parentDir + " " +extractMountPoint); + result = command.execute(); + if (result != null) { + s_logger.warn("Error in linking err=" + result ); + return; + } + + } + + @Override + public String getName() { + return _name; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } /** * Get notified of change of job status. Executed in context of uploader thread @@ -582,7 +585,7 @@ public class UploadManagerImpl implements UploadManager { tu.setStatus(Status.UNRECOVERABLE_ERROR); tu.setUploadError("Failed post upload script: " + result); } else { - s_logger.warn("Upload completed successfully at " + new SimpleDateFormat().format(new Date())); + s_logger.warn("Upload completed successfully at " + new SimpleDateFormat().format(new Date())); tu.setStatus(Status.POST_UPLOAD_FINISHED); tu.setUploadError("Upload completed successfully at " + new SimpleDateFormat().format(new Date())); } @@ -596,9 +599,9 @@ public class UploadManagerImpl implements UploadManager { } } - private String postUpload(String jobId) { - return null; - } + private String postUpload(String jobId) { + return null; + } private void sleep() { try { @@ -608,21 +611,21 @@ public class UploadManagerImpl implements UploadManager { } } - private boolean checkAndStartApache() { - - //Check whether the Apache server is running - Script command = new Script("/bin/bash", s_logger); - command.add("-c"); - command.add("if [ -d /etc/apache2 ] ; then service apache2 status | grep pid; else service httpd status | grep pid; fi "); - String result = command.execute(); - - //Apache Server is not running. Try to start it. - if (result != null) { - - /*s_logger.warn("Apache server not running, trying to start it"); + private boolean checkAndStartApache() { + + //Check whether the Apache server is running + Script command = new Script("/bin/bash", s_logger); + command.add("-c"); + command.add("if [ -d /etc/apache2 ] ; then service apache2 status | grep pid; else service httpd status | grep pid; fi "); + String result = command.execute(); + + //Apache Server is not running. Try to start it. + if (result != null) { + + /*s_logger.warn("Apache server not running, trying to start it"); String port = Integer.toString(TemplateConstants.DEFAULT_TMPLT_COPY_PORT); String intf = TemplateConstants.DEFAULT_TMPLT_COPY_INTF; - + command = new Script("/bin/bash", s_logger); command.add("-c"); command.add("iptables -D INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + port + " -j DROP;" + @@ -636,23 +639,23 @@ public class UploadManagerImpl implements UploadManager { "iptables -I INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + "443" + " -j DROP;" + "iptables -I INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + port + " -j HTTP;" + "iptables -I INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + "443" + " -j HTTP;"); - + result = command.execute(); if (result != null) { s_logger.warn("Error in opening up httpd port err=" + result ); return false; }*/ - - command = new Script("/bin/bash", s_logger); - command.add("-c"); - command.add("if [ -d /etc/apache2 ] ; then service apache2 start; else service httpd start; fi "); - result = command.execute(); - if (result != null) { - s_logger.warn("Error in starting httpd service err=" + result ); - return false; - } - } - - return true; - } + + command = new Script("/bin/bash", s_logger); + command.add("-c"); + command.add("if [ -d /etc/apache2 ] ; then service apache2 start; else service httpd start; fi "); + result = command.execute(); + if (result != null) { + s_logger.warn("Error in starting httpd service err=" + result ); + return false; + } + } + + return true; + } } diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java index 2cc72dd11b7..26cd5592bab 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java @@ -8,7 +8,7 @@ import org.apache.cloudstack.storage.snapshot.SnapshotInfo; import org.apache.cloudstack.storage.volume.ObjectInDataStoreStateMachine.Event; import org.springframework.stereotype.Component; -import com.cloud.utils.component.Inject; + @Component public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager { diff --git a/engine/storage/src/org/apache/cloudstack/storage/volume/db/VolumeDao2Impl.java b/engine/storage/src/org/apache/cloudstack/storage/volume/db/VolumeDao2Impl.java index 0eb0ac30d5c..1e12498dff6 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/volume/db/VolumeDao2Impl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/volume/db/VolumeDao2Impl.java @@ -38,7 +38,7 @@ import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Volume; import com.cloud.tags.dao.ResourceTagsDaoImpl; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; diff --git a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/AssociateLunCmd.java b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/AssociateLunCmd.java index c87c9242010..64865bb0777 100644 --- a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/AssociateLunCmd.java +++ b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/AssociateLunCmd.java @@ -29,7 +29,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.netapp.NetappManager; import com.cloud.server.ManagementService; import com.cloud.server.api.response.netapp.AssociateLunCmdResponse; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "associateLun", description="Associate a LUN with a guest IQN", responseObject = AssociateLunCmdResponse.class) public class AssociateLunCmd extends BaseCmd { diff --git a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/CreateLunCmd.java b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/CreateLunCmd.java index 8c89730b978..c8d8d04e6f1 100644 --- a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/CreateLunCmd.java +++ b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/CreateLunCmd.java @@ -33,7 +33,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.netapp.NetappManager; import com.cloud.server.ManagementService; import com.cloud.server.api.response.netapp.CreateLunCmdResponse; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "createLunOnFiler", description="Create a LUN from a pool", responseObject = CreateLunCmdResponse.class) public class CreateLunCmd extends BaseCmd { diff --git a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/CreateVolumeOnFilerCmd.java b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/CreateVolumeOnFilerCmd.java index a2d4b96e6dd..72a9efae978 100644 --- a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/CreateVolumeOnFilerCmd.java +++ b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/CreateVolumeOnFilerCmd.java @@ -32,7 +32,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.netapp.NetappManager; import com.cloud.server.ManagementService; import com.cloud.server.api.response.netapp.CreateVolumeOnFilerCmdResponse; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "createVolumeOnFiler", description="Create a volume", responseObject = CreateVolumeOnFilerCmdResponse.class) public class CreateVolumeOnFilerCmd extends BaseCmd { diff --git a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/CreateVolumePoolCmd.java b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/CreateVolumePoolCmd.java index 9e38c5fc097..f7ff567e838 100644 --- a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/CreateVolumePoolCmd.java +++ b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/CreateVolumePoolCmd.java @@ -31,7 +31,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.netapp.NetappManager; import com.cloud.server.ManagementService; import com.cloud.server.api.response.netapp.CreateVolumePoolCmdResponse; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "createPool", description="Create a pool", responseObject = CreateVolumePoolCmdResponse.class) public class CreateVolumePoolCmd extends BaseCmd { diff --git a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DeleteVolumePoolCmd.java b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DeleteVolumePoolCmd.java index 1105ea53e9d..7106c580125 100644 --- a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DeleteVolumePoolCmd.java +++ b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DeleteVolumePoolCmd.java @@ -33,7 +33,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.netapp.NetappManager; import com.cloud.server.ManagementService; import com.cloud.server.api.response.netapp.DeleteVolumePoolCmdResponse; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "deletePool", description="Delete a pool", responseObject = DeleteVolumePoolCmdResponse.class) public class DeleteVolumePoolCmd extends BaseCmd { diff --git a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DestroyLunCmd.java b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DestroyLunCmd.java index c5f7b117f5b..8afd14342d4 100644 --- a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DestroyLunCmd.java +++ b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DestroyLunCmd.java @@ -33,7 +33,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.netapp.NetappManager; import com.cloud.server.ManagementService; import com.cloud.server.api.response.netapp.DeleteLUNCmdResponse; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "destroyLunOnFiler", description="Destroy a LUN", responseObject = DeleteLUNCmdResponse.class) public class DestroyLunCmd extends BaseCmd { diff --git a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DestroyVolumeOnFilerCmd.java b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DestroyVolumeOnFilerCmd.java index 4ddc0c9f6d0..730f1c0bb43 100644 --- a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DestroyVolumeOnFilerCmd.java +++ b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DestroyVolumeOnFilerCmd.java @@ -31,7 +31,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.netapp.NetappManager; import com.cloud.server.ManagementService; import com.cloud.server.api.response.netapp.DeleteVolumeOnFilerCmdResponse; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "destroyVolumeOnFiler", description="Destroy a Volume", responseObject = DeleteVolumeOnFilerCmdResponse.class) public class DestroyVolumeOnFilerCmd extends BaseCmd { diff --git a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DissociateLunCmd.java b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DissociateLunCmd.java index 0a6c1a70ef1..5061f497521 100644 --- a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DissociateLunCmd.java +++ b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/DissociateLunCmd.java @@ -30,7 +30,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.netapp.NetappManager; import com.cloud.server.ManagementService; import com.cloud.server.api.response.netapp.DissociateLunCmdResponse; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "dissociateLun", description="Dissociate a LUN", responseObject = DissociateLunCmdResponse.class) public class DissociateLunCmd extends BaseCmd { diff --git a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ListLunsCmd.java b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ListLunsCmd.java index 630b14994e7..7c2ed45ebc3 100644 --- a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ListLunsCmd.java +++ b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ListLunsCmd.java @@ -36,7 +36,7 @@ import com.cloud.netapp.LunVO; import com.cloud.netapp.NetappManager; import com.cloud.server.ManagementService; import com.cloud.server.api.response.netapp.ListLunsCmdResponse; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "listLunsOnFiler", description="List LUN", responseObject = ListLunsCmdResponse.class) public class ListLunsCmd extends BaseCmd diff --git a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ListVolumePoolsCmd.java b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ListVolumePoolsCmd.java index d77f4fad849..5857f4340af 100644 --- a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ListVolumePoolsCmd.java +++ b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ListVolumePoolsCmd.java @@ -34,7 +34,7 @@ import com.cloud.netapp.NetappManager; import com.cloud.netapp.PoolVO; import com.cloud.server.ManagementService; import com.cloud.server.api.response.netapp.ListVolumePoolsCmdResponse; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "listPools", description="List Pool", responseObject = ListVolumePoolsCmdResponse.class) public class ListVolumePoolsCmd extends BaseCmd { diff --git a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ListVolumesOnFilerCmd.java b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ListVolumesOnFilerCmd.java index 66a96f3a221..17548cd65be 100644 --- a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ListVolumesOnFilerCmd.java +++ b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ListVolumesOnFilerCmd.java @@ -33,7 +33,7 @@ import com.cloud.netapp.NetappManager; import com.cloud.netapp.NetappVolumeVO; import com.cloud.server.ManagementService; import com.cloud.server.api.response.netapp.ListVolumesOnFilerCmdResponse; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "listVolumesOnFiler", description="List Volumes", responseObject = ListVolumesOnFilerCmdResponse.class) public class ListVolumesOnFilerCmd extends BaseCmd { diff --git a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ModifyVolumePoolCmd.java b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ModifyVolumePoolCmd.java index 3e32caebef3..6282a648a99 100644 --- a/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ModifyVolumePoolCmd.java +++ b/plugins/file-systems/netapp/src/com/cloud/api/commands/netapp/ModifyVolumePoolCmd.java @@ -31,7 +31,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.netapp.NetappManager; import com.cloud.server.ManagementService; import com.cloud.server.api.response.netapp.ModifyVolumePoolCmdResponse; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "modifyPool", description="Modify pool", responseObject = ModifyVolumePoolCmdResponse.class) public class ModifyVolumePoolCmd extends BaseCmd { diff --git a/plugins/host-allocators/random/src/com/cloud/agent/manager/allocator/impl/RandomAllocator.java b/plugins/host-allocators/random/src/com/cloud/agent/manager/allocator/impl/RandomAllocator.java index 241b114589a..c302cdd293f 100755 --- a/plugins/host-allocators/random/src/com/cloud/agent/manager/allocator/impl/RandomAllocator.java +++ b/plugins/host-allocators/random/src/com/cloud/agent/manager/allocator/impl/RandomAllocator.java @@ -36,7 +36,6 @@ import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.offering.ServiceOffering; import com.cloud.resource.ResourceManager; -import com.cloud.utils.component.ComponentLocator; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; @@ -53,39 +52,39 @@ public class RandomAllocator implements HostAllocator { ExcludeList avoid, int returnUpTo) { return allocateTo(vmProfile, plan, type, avoid, returnUpTo, true); } - + @Override public List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, - ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity) { + ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity) { + + long dcId = plan.getDataCenterId(); + Long podId = plan.getPodId(); + Long clusterId = plan.getClusterId(); + ServiceOffering offering = vmProfile.getServiceOffering(); + + List suitableHosts = new ArrayList(); - long dcId = plan.getDataCenterId(); - Long podId = plan.getPodId(); - Long clusterId = plan.getClusterId(); - ServiceOffering offering = vmProfile.getServiceOffering(); - - List suitableHosts = new ArrayList(); - if (type == Host.Type.Storage) { return suitableHosts; } String hostTag = offering.getHostTag(); if(hostTag != null){ - s_logger.debug("Looking for hosts in dc: " + dcId + " pod:" + podId + " cluster:" + clusterId + " having host tag:" + hostTag); + s_logger.debug("Looking for hosts in dc: " + dcId + " pod:" + podId + " cluster:" + clusterId + " having host tag:" + hostTag); }else{ - s_logger.debug("Looking for hosts in dc: " + dcId + " pod:" + podId + " cluster:" + clusterId); + s_logger.debug("Looking for hosts in dc: " + dcId + " pod:" + podId + " cluster:" + clusterId); } // list all computing hosts, regardless of whether they support routing...it's random after all List hosts = new ArrayList(); if(hostTag != null){ - hosts = _hostDao.listByHostTag(type, clusterId, podId, dcId, hostTag); + hosts = _hostDao.listByHostTag(type, clusterId, podId, dcId, hostTag); }else{ - hosts = _resourceMgr.listAllUpAndEnabledHosts(type, clusterId, podId, dcId); + hosts = _resourceMgr.listAllUpAndEnabledHosts(type, clusterId, podId, dcId); } - + s_logger.debug("Random Allocator found " + hosts.size() + " hosts"); - + if (hosts.size() == 0) { return suitableHosts; } @@ -93,12 +92,12 @@ public class RandomAllocator implements HostAllocator { Collections.shuffle(hosts); for (Host host : hosts) { - if(suitableHosts.size() == returnUpTo){ - break; - } - + if(suitableHosts.size() == returnUpTo){ + break; + } + if (!avoid.shouldAvoid(host)) { - suitableHosts.add(host); + suitableHosts.add(host); }else{ if (s_logger.isDebugEnabled()) { s_logger.debug("Host name: " + host.getName() + ", hostId: "+ host.getId() +" is in avoid set, skipping this and trying other available hosts"); @@ -121,7 +120,7 @@ public class RandomAllocator implements HostAllocator { @Override public boolean configure(String name, Map params) { _name=name; - + return true; } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index b52e2d8a0b0..cd1971dd427 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -212,7 +212,7 @@ import com.cloud.storage.template.TemplateLocation; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.utils.script.OutputInterpreter; diff --git a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockAgentManagerImpl.java b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockAgentManagerImpl.java index f6bc8fc7fee..506fbe02d02 100755 --- a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockAgentManagerImpl.java +++ b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockAgentManagerImpl.java @@ -58,7 +58,7 @@ import com.cloud.simulator.MockVMVO; import com.cloud.simulator.dao.MockHostDao; import com.cloud.simulator.dao.MockVMDao; import com.cloud.utils.Pair; -import com.cloud.utils.component.Inject; + import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; diff --git a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockStorageManagerImpl.java b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockStorageManagerImpl.java index 1076089dcd6..f13925440c8 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockStorageManagerImpl.java +++ b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockStorageManagerImpl.java @@ -77,7 +77,7 @@ import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; import com.cloud.storage.template.TemplateInfo; -import com.cloud.utils.component.Inject; + import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.DiskProfile; diff --git a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java index 40cd80acf8e..69c066d4577 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java +++ b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java @@ -46,7 +46,7 @@ import com.cloud.simulator.dao.MockSecurityRulesDao; import com.cloud.simulator.dao.MockVMDao; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; -import com.cloud.utils.component.Inject; + import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine.State; diff --git a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java index 2bed2efec6a..25e7d481a06 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java +++ b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java @@ -28,7 +28,7 @@ import com.cloud.simulator.MockVMVO; import com.cloud.simulator.dao.MockConfigurationDao; import com.cloud.simulator.dao.MockHostDao; import com.cloud.utils.Pair; -import com.cloud.utils.component.Inject; + import com.cloud.utils.db.ConnectionConcierge; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; diff --git a/plugins/hypervisors/simulator/src/com/cloud/api/commands/ConfigureSimulator.java b/plugins/hypervisors/simulator/src/com/cloud/api/commands/ConfigureSimulator.java index df81249538d..e4e76bb65f9 100755 --- a/plugins/hypervisors/simulator/src/com/cloud/api/commands/ConfigureSimulator.java +++ b/plugins/hypervisors/simulator/src/com/cloud/api/commands/ConfigureSimulator.java @@ -31,7 +31,7 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.server.ManagementService; import com.cloud.user.Account; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "configureSimulator", description="configure simulator", responseObject=SuccessResponse.class) public class ConfigureSimulator extends BaseCmd { diff --git a/plugins/hypervisors/simulator/src/com/cloud/resource/AgentResourceBase.java b/plugins/hypervisors/simulator/src/com/cloud/resource/AgentResourceBase.java index 808ca070d4d..ee6d8a34825 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/resource/AgentResourceBase.java +++ b/plugins/hypervisors/simulator/src/com/cloud/resource/AgentResourceBase.java @@ -43,7 +43,7 @@ import com.cloud.agent.manager.SimulatorManager.AgentType; import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.simulator.MockHost; -import com.cloud.utils.component.ComponentLocator; + public class AgentResourceBase implements ServerResource { private static final Logger s_logger = Logger diff --git a/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorDiscoverer.java b/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorDiscoverer.java index b6d40d49589..69ada1467ef 100755 --- a/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorDiscoverer.java +++ b/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorDiscoverer.java @@ -54,7 +54,7 @@ import com.cloud.storage.VMTemplateZoneVO; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateHostDao; import com.cloud.storage.dao.VMTemplateZoneDao; -import com.cloud.utils.component.Inject; + @Local(value = Discoverer.class) public class SimulatorDiscoverer extends DiscovererBase implements Discoverer, Listener, ResourceStateAdapter { diff --git a/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorSecondaryDiscoverer.java b/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorSecondaryDiscoverer.java index 3f7cea5b6b1..3b22ad51fba 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorSecondaryDiscoverer.java +++ b/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorSecondaryDiscoverer.java @@ -38,7 +38,7 @@ import com.cloud.host.Status; import com.cloud.storage.SnapshotVO; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.secondary.SecondaryStorageDiscoverer; -import com.cloud.utils.component.Inject; + import com.cloud.utils.exception.CloudRuntimeException; import org.apache.log4j.Logger; diff --git a/plugins/hypervisors/simulator/src/com/cloud/simulator/SimulatorGuru.java b/plugins/hypervisors/simulator/src/com/cloud/simulator/SimulatorGuru.java index b9c404b66a1..102ce4ec1cc 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/simulator/SimulatorGuru.java +++ b/plugins/hypervisors/simulator/src/com/cloud/simulator/SimulatorGuru.java @@ -24,7 +24,7 @@ import com.cloud.hypervisor.HypervisorGuruBase; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.GuestOSVO; import com.cloud.storage.dao.GuestOSDao; -import com.cloud.utils.component.Inject; + import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; diff --git a/plugins/hypervisors/simulator/src/com/cloud/simulator/dao/MockVMDaoImpl.java b/plugins/hypervisors/simulator/src/com/cloud/simulator/dao/MockVMDaoImpl.java index 86264f2c039..e5b30f04c25 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/simulator/dao/MockVMDaoImpl.java +++ b/plugins/hypervisors/simulator/src/com/cloud/simulator/dao/MockVMDaoImpl.java @@ -25,7 +25,7 @@ import javax.naming.ConfigurationException; import com.cloud.simulator.MockHostVO; import com.cloud.simulator.MockVMVO; -import com.cloud.utils.component.Inject; + import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareServerDiscoverer.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareServerDiscoverer.java index b5d58dc2619..7286ada5798 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareServerDiscoverer.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/VmwareServerDiscoverer.java @@ -66,7 +66,7 @@ import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.user.Account; import com.cloud.utils.UriUtils; -import com.cloud.utils.component.ComponentLocator; + import com.vmware.vim25.ClusterDasConfigInfo; import com.vmware.vim25.ManagedObjectReference; diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index 75e10c9f36a..618d996f955 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -86,7 +86,7 @@ import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.utils.FileUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java index 053ed6eaf46..7ec06575208 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareContextFactory.java @@ -21,7 +21,7 @@ import org.apache.log4j.Logger; import com.cloud.hypervisor.vmware.manager.VmwareManager; import com.cloud.hypervisor.vmware.util.VmwareContext; import com.cloud.utils.StringUtils; -import com.cloud.utils.component.ComponentLocator; + import com.vmware.apputils.version.ExtendedAppUtil; public class VmwareContextFactory { diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index a444cfec197..47fcb86b5c9 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -200,7 +200,7 @@ import com.cloud.storage.template.TemplateInfo; import com.cloud.utils.DateUtil; import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DB; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.ExceptionUtil; diff --git a/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/dao/ElasticLbVmMapDaoImpl.java b/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/dao/ElasticLbVmMapDaoImpl.java index ba8c82d01c2..be61f580f75 100644 --- a/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/dao/ElasticLbVmMapDaoImpl.java +++ b/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/dao/ElasticLbVmMapDaoImpl.java @@ -30,7 +30,7 @@ import com.cloud.network.dao.LoadBalancerDao; import com.cloud.network.dao.LoadBalancerDaoImpl; import com.cloud.network.router.VirtualRouter.Role; import com.cloud.network.router.VirtualRouter.Role; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.JoinBuilder.JoinType; import com.cloud.utils.db.SearchBuilder; diff --git a/plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java b/plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java index 9d217e6f2c9..43d35662ee9 100644 --- a/plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java +++ b/plugins/user-authenticators/md5/src/com/cloud/server/auth/MD5UserAuthenticator.java @@ -30,7 +30,7 @@ import org.springframework.stereotype.Component; import com.cloud.server.ManagementServer; import com.cloud.user.UserAccount; import com.cloud.user.dao.UserAccountDao; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.exception.CloudRuntimeException; /** diff --git a/plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java b/plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java index 0bf650bfe53..d2f43471366 100644 --- a/plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java +++ b/plugins/user-authenticators/plain-text/src/com/cloud/server/auth/PlainTextUserAuthenticator.java @@ -30,7 +30,7 @@ import org.springframework.stereotype.Component; import com.cloud.server.ManagementServer; import com.cloud.user.UserAccount; import com.cloud.user.dao.UserAccountDao; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.exception.CloudRuntimeException; diff --git a/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java b/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java index 26c33a5a9ec..28ed92ded0c 100644 --- a/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java +++ b/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java @@ -32,8 +32,8 @@ import com.cloud.server.ManagementServer; import com.cloud.servlet.CloudStartupServlet; import com.cloud.user.UserAccount; import com.cloud.user.dao.UserAccountDao; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.component.Inject; + + import com.cloud.utils.exception.CloudRuntimeException; @Local(value={UserAuthenticator.class}) diff --git a/server/src/com/cloud/agent/manager/AgentManagerImpl.java b/server/src/com/cloud/agent/manager/AgentManagerImpl.java index f7ca0349450..77f131ad222 100755 --- a/server/src/com/cloud/agent/manager/AgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/AgentManagerImpl.java @@ -66,7 +66,6 @@ import com.cloud.agent.transport.Response; import com.cloud.alert.AlertManager; import com.cloud.capacity.dao.CapacityDao; import com.cloud.cluster.ManagementServerNode; -import com.cloud.cluster.StackMaid; import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.ClusterDetailsDao; @@ -977,8 +976,6 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } } catch (final Exception e) { s_logger.error("Exception caught while handling disconnect: ", e); - } finally { - StackMaid.current().exitCleanup(); } } } @@ -1150,7 +1147,6 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { if (actionDelegate != null) { actionDelegate.action(new Long(id)); } - StackMaid.current().exitCleanup(); } } } @@ -1381,7 +1377,6 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager { } } } finally { - StackMaid.current().exitCleanup(); txn.close(); } } diff --git a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java index dbd7cd3ed47..25c71687bed 100755 --- a/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java +++ b/server/src/com/cloud/agent/manager/ClusteredAgentManagerImpl.java @@ -62,7 +62,6 @@ import com.cloud.cluster.ClusterManagerListener; import com.cloud.cluster.ClusteredAgentRebalanceService; import com.cloud.cluster.ManagementServerHost; import com.cloud.cluster.ManagementServerHostVO; -import com.cloud.cluster.StackMaid; import com.cloud.cluster.agentlb.AgentLoadBalancerPlanner; import com.cloud.cluster.agentlb.HostTransferMapVO; import com.cloud.cluster.agentlb.HostTransferMapVO.HostTransferState; @@ -1120,8 +1119,6 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust rebalanceHost(hostId, currentOwnerId, futureOwnerId); } catch (Exception e) { s_logger.warn("Unable to rebalance host id=" + hostId, e); - } finally { - StackMaid.current().exitCleanup(); } } } diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 6e13f13fbdf..b6f82c3e8e6 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -104,12 +104,12 @@ import org.apache.http.protocol.ResponseContent; import org.apache.http.protocol.ResponseDate; import org.apache.http.protocol.ResponseServer; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.api.response.ApiResponseSerializer; import com.cloud.async.AsyncJob; import com.cloud.async.AsyncJobManager; import com.cloud.async.AsyncJobVO; -import com.cloud.cluster.StackMaid; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationVO; import com.cloud.configuration.dao.ConfigurationDao; @@ -136,19 +136,20 @@ import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CSExceptionErrorCode; import com.cloud.uuididentity.dao.IdentityDao; +@Component public class ApiServer implements HttpRequestHandler { private static final Logger s_logger = Logger.getLogger(ApiServer.class.getName()); private static final Logger s_accessLogger = Logger.getLogger("apiserver." + ApiServer.class.getName()); public static boolean encodeApiResponse = false; public static String jsonContentType = "text/javascript"; - private ApiDispatcher _dispatcher; + @Inject ApiDispatcher _dispatcher; - @Inject private final AccountManager _accountMgr = null; - @Inject private final DomainManager _domainMgr = null; - @Inject private final AsyncJobManager _asyncMgr = null; - @Inject private ConfigurationDao _configDao; - @Inject protected List _apiAccessCheckers; + @Inject AccountManager _accountMgr; + @Inject DomainManager _domainMgr; + @Inject AsyncJobManager _asyncMgr; + @Inject ConfigurationDao _configDao; + @Inject List _apiAccessCheckers; @Inject List _pluggableServices; @Inject IdentityDao _identityDao; @@ -190,7 +191,6 @@ public class ApiServer implements HttpRequestHandler { _systemAccount = _accountMgr.getSystemAccount(); _systemUser = _accountMgr.getSystemUser(); - _dispatcher = ApiDispatcher.getInstance(); Integer apiPort = null; // api port, null by default SearchCriteria sc = _configDao.createSearchCriteria(); @@ -907,12 +907,8 @@ public class ApiServer implements HttpRequestHandler { HttpContext context = new BasicHttpContext(null); try { while (!Thread.interrupted() && _conn.isOpen()) { - try { - _httpService.handleRequest(_conn, context); - _conn.close(); - } finally { - StackMaid.current().exitCleanup(); - } + _httpService.handleRequest(_conn, context); + _conn.close(); } } catch (ConnectionClosedException ex) { if (s_logger.isTraceEnabled()) { diff --git a/server/src/com/cloud/api/ApiServlet.java b/server/src/com/cloud/api/ApiServlet.java index 19091f25ff2..1a8a04ee237 100755 --- a/server/src/com/cloud/api/ApiServlet.java +++ b/server/src/com/cloud/api/ApiServlet.java @@ -23,6 +23,7 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.Map; +import javax.inject.Inject; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -32,14 +33,11 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.ServerApiException; import org.apache.log4j.Logger; -import com.cloud.cluster.StackMaid; import com.cloud.exception.CloudAuthenticationException; -import com.cloud.server.ManagementServer; import com.cloud.user.Account; import com.cloud.user.AccountService; import com.cloud.user.UserContext; import com.cloud.utils.StringUtils; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.exception.CloudRuntimeException; @SuppressWarnings("serial") @@ -47,8 +45,8 @@ public class ApiServlet extends HttpServlet { public static final Logger s_logger = Logger.getLogger(ApiServlet.class.getName()); private static final Logger s_accessLogger = Logger.getLogger("apiserver." + ApiServer.class.getName()); - private ApiServer _apiServer = null; - private AccountService _accountMgr = null; + ApiServer _apiServer; + @Inject AccountService _accountMgr; public ApiServlet() { super(); @@ -56,26 +54,16 @@ public class ApiServlet extends HttpServlet { if (_apiServer == null) { throw new CloudRuntimeException("ApiServer not initialized"); } - ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); - _accountMgr = locator.getManager(AccountService.class); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { - try { - processRequest(req, resp); - } finally { - StackMaid.current().exitCleanup(); - } + processRequest(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) { - try { - processRequest(req, resp); - } finally { - StackMaid.current().exitCleanup(); - } + processRequest(req, resp); } private void utf8Fixup(HttpServletRequest req, Map params) { @@ -128,7 +116,7 @@ public class ApiServlet extends HttpServlet { reqStr = auditTrailSb.toString() + " " + req.getQueryString(); s_logger.debug("===START=== " + StringUtils.cleanString(reqStr)); } - + try { HttpSession session = req.getSession(false); Object[] responseTypeParam = params.get("response"); @@ -305,7 +293,7 @@ public class ApiServlet extends HttpServlet { auditTrailSb.insert(0, "(userId=" + UserContext.current().getCallerUserId() + " accountId=" + UserContext.current().getCaller().getId() + " sessionId=" + (session != null ? session.getId() : null) - + ")"); + + ")"); try { String response = _apiServer.handleRequest(params, false, responseType, auditTrailSb); @@ -386,7 +374,7 @@ public class ApiServlet extends HttpServlet { private String getLoginSuccessResponse(HttpSession session, String responseType) { StringBuffer sb = new StringBuffer(); int inactiveInterval = session.getMaxInactiveInterval(); - + String user_UUID = (String)session.getAttribute("user_UUID"); session.removeAttribute("user_UUID"); diff --git a/server/src/com/cloud/api/commands/AddTrafficMonitorCmd.java b/server/src/com/cloud/api/commands/AddTrafficMonitorCmd.java index fdbbbe4ca3c..c80a62b3b01 100644 --- a/server/src/com/cloud/api/commands/AddTrafficMonitorCmd.java +++ b/server/src/com/cloud/api/commands/AddTrafficMonitorCmd.java @@ -16,91 +16,93 @@ // under the License. package com.cloud.api.commands; -import org.apache.cloudstack.api.*; +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.TrafficMonitorResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.log4j.Logger; -import org.apache.cloudstack.api.APICommand; import com.cloud.exception.InvalidParameterValueException; import com.cloud.host.Host; import com.cloud.network.NetworkUsageManager; -import com.cloud.server.ManagementService; -import org.apache.cloudstack.api.response.TrafficMonitorResponse; import com.cloud.user.Account; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.exception.CloudRuntimeException; @APICommand(name = "addTrafficMonitor", description="Adds Traffic Monitor Host for Direct Network Usage", responseObject = TrafficMonitorResponse.class) public class AddTrafficMonitorCmd extends BaseCmd { - public static final Logger s_logger = Logger.getLogger(AddTrafficMonitorCmd.class.getName()); - private static final String s_name = "addtrafficmonitorresponse"; - - ///////////////////////////////////////////////////// + public static final Logger s_logger = Logger.getLogger(AddTrafficMonitorCmd.class.getName()); + private static final String s_name = "addtrafficmonitorresponse"; + @Inject NetworkUsageManager networkUsageMgr; + + ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - - @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneResponse.class, + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneResponse.class, required = true, description="Zone in which to add the external firewall appliance.") - private Long zoneId; - - @Parameter(name=ApiConstants.URL, type=CommandType.STRING, required = true, description="URL of the traffic monitor Host") - private String url; + private Long zoneId; - @Parameter(name=ApiConstants.INCL_ZONES, type=CommandType.STRING, description="Traffic going into the listed zones will be metered") - private String inclZones; - - @Parameter(name=ApiConstants.EXCL_ZONES, type=CommandType.STRING, description="Traffic going into the listed zones will not be metered") - private String exclZones; - - /////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - public String getInclZones() { - return inclZones; - } - - public String getExclZones() { - return exclZones; - } + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, required = true, description="URL of the traffic monitor Host") + private String url; - public Long getZoneId() { - return zoneId; - } + @Parameter(name=ApiConstants.INCL_ZONES, type=CommandType.STRING, description="Traffic going into the listed zones will be metered") + private String inclZones; - public String getUrl() { - return url; - } - - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// + @Parameter(name=ApiConstants.EXCL_ZONES, type=CommandType.STRING, description="Traffic going into the listed zones will not be metered") + private String exclZones; - @Override - public String getCommandName() { - return s_name; - } - - @Override + /////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getInclZones() { + return inclZones; + } + + public String getExclZones() { + return exclZones; + } + + public Long getZoneId() { + return zoneId; + } + + public String getUrl() { + return url; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override public long getEntityOwnerId() { return Account.ACCOUNT_ID_SYSTEM; } - - @Override + + @Override public void execute(){ - try { - ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); - NetworkUsageManager networkUsageMgr = locator.getManager(NetworkUsageManager.class); - Host trafficMonitor = networkUsageMgr.addTrafficMonitor(this); - TrafficMonitorResponse response = networkUsageMgr.getApiResponse(trafficMonitor); - response.setObjectName("trafficmonitor"); - response.setResponseName(getCommandName()); - this.setResponseObject(response); - } catch (InvalidParameterValueException ipve) { - throw new ServerApiException(BaseCmd.PARAM_ERROR, ipve.getMessage()); - } catch (CloudRuntimeException cre) { - throw new ServerApiException(BaseCmd.INTERNAL_ERROR, cre.getMessage()); - } + try { + Host trafficMonitor = networkUsageMgr.addTrafficMonitor(this); + TrafficMonitorResponse response = networkUsageMgr.getApiResponse(trafficMonitor); + response.setObjectName("trafficmonitor"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException ipve) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, ipve.getMessage()); + } catch (CloudRuntimeException cre) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, cre.getMessage()); + } } } diff --git a/server/src/com/cloud/api/commands/DeleteTrafficMonitorCmd.java b/server/src/com/cloud/api/commands/DeleteTrafficMonitorCmd.java index 4c7d3a70546..9e84f03d8e9 100644 --- a/server/src/com/cloud/api/commands/DeleteTrafficMonitorCmd.java +++ b/server/src/com/cloud/api/commands/DeleteTrafficMonitorCmd.java @@ -16,71 +16,70 @@ // under the License. package com.cloud.api.commands; -import org.apache.cloudstack.api.response.HostResponse; -import org.apache.log4j.Logger; +import javax.inject.Inject; +import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.NetworkUsageManager; -import com.cloud.server.ManagementService; import com.cloud.user.Account; -import com.cloud.utils.component.ComponentLocator; @APICommand(name = "deleteTrafficMonitor", description="Deletes an traffic monitor host.", responseObject = SuccessResponse.class) public class DeleteTrafficMonitorCmd extends BaseCmd { - public static final Logger s_logger = Logger.getLogger(DeleteTrafficMonitorCmd.class.getName()); - private static final String s_name = "deletetrafficmonitorresponse"; - - ///////////////////////////////////////////////////// + public static final Logger s_logger = Logger.getLogger(DeleteTrafficMonitorCmd.class.getName()); + private static final String s_name = "deletetrafficmonitorresponse"; + @Inject NetworkUsageManager _networkUsageMgr; + + ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - - @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = HostResponse.class, - required = true, description="Id of the Traffic Monitor Host.") - private Long id; - - /////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - public Long getId() { - return id; - } - - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - @Override - public String getCommandName() { - return s_name; - } - - @Override + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = HostResponse.class, + required = true, description="Id of the Traffic Monitor Host.") + private Long id; + + /////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override public long getEntityOwnerId() { return Account.ACCOUNT_ID_SYSTEM; } - - @Override + + @Override public void execute(){ - try { - ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); - NetworkUsageManager _networkUsageMgr = locator.getManager(NetworkUsageManager.class); - boolean result = _networkUsageMgr.deleteTrafficMonitor(this); - if (result) { - SuccessResponse response = new SuccessResponse(getCommandName()); - response.setResponseName(getCommandName()); - this.setResponseObject(response); - } else { - throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to delete traffic monitor."); - } - } catch (InvalidParameterValueException e) { - throw new ServerApiException(BaseCmd.PARAM_ERROR, "Failed to delete traffic monitor."); - } + try { + boolean result = _networkUsageMgr.deleteTrafficMonitor(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to delete traffic monitor."); + } + } catch (InvalidParameterValueException e) { + throw new ServerApiException(BaseCmd.PARAM_ERROR, "Failed to delete traffic monitor."); + } } } diff --git a/server/src/com/cloud/api/commands/ListTrafficMonitorsCmd.java b/server/src/com/cloud/api/commands/ListTrafficMonitorsCmd.java index 21ad339137a..02f51a8d002 100644 --- a/server/src/com/cloud/api/commands/ListTrafficMonitorsCmd.java +++ b/server/src/com/cloud/api/commands/ListTrafficMonitorsCmd.java @@ -32,7 +32,7 @@ import com.cloud.host.Host; import com.cloud.network.NetworkUsageManager; import com.cloud.server.ManagementService; import org.apache.cloudstack.api.response.TrafficMonitorResponse; -import com.cloud.utils.component.ComponentLocator; + @APICommand(name = "listTrafficMonitors", description="List traffic monitor Hosts.", responseObject = TrafficMonitorResponse.class) public class ListTrafficMonitorsCmd extends BaseListCmd { diff --git a/server/src/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java index cdfac3a8247..96b91df79f9 100644 --- a/server/src/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java @@ -20,21 +20,19 @@ import java.util.ArrayList; import java.util.List; import javax.ejb.Local; +import javax.inject.Inject; +import org.apache.cloudstack.api.response.DomainRouterResponse; +import org.apache.cloudstack.api.response.NicResponse; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.api.ApiResponseHelper; import com.cloud.api.query.vo.DomainRouterJoinVO; import com.cloud.configuration.dao.ConfigurationDao; - -import org.apache.cloudstack.api.response.DomainRouterResponse; -import org.apache.cloudstack.api.response.NicResponse; -import org.springframework.stereotype.Component; - import com.cloud.network.Networks.TrafficType; import com.cloud.network.router.VirtualRouter; import com.cloud.user.Account; -import com.cloud.utils.component.Inject; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -47,9 +45,9 @@ public class DomainRouterJoinDaoImpl extends GenericDaoBase vrSearch; + private final SearchBuilder vrSearch; - private SearchBuilder vrIdSearch; + private final SearchBuilder vrIdSearch; protected DomainRouterJoinDaoImpl() { diff --git a/server/src/com/cloud/api/query/dao/HostJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/HostJoinDaoImpl.java index 9a7ba468227..fa7618cbd3f 100644 --- a/server/src/com/cloud/api/query/dao/HostJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/HostJoinDaoImpl.java @@ -25,21 +25,19 @@ import java.util.List; import java.util.Set; import javax.ejb.Local; +import javax.inject.Inject; +import org.apache.cloudstack.api.ApiConstants.HostDetails; +import org.apache.cloudstack.api.response.HostResponse; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.api.ApiDBUtils; import com.cloud.api.query.vo.HostJoinVO; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.host.Host; import com.cloud.host.HostStats; - -import org.apache.cloudstack.api.ApiConstants.HostDetails; -import org.apache.cloudstack.api.response.HostResponse; -import org.springframework.stereotype.Component; - import com.cloud.storage.StorageStats; -import com.cloud.utils.component.Inject; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -52,9 +50,9 @@ public class HostJoinDaoImpl extends GenericDaoBase implements @Inject private ConfigurationDao _configDao; - private SearchBuilder hostSearch; + private final SearchBuilder hostSearch; - private SearchBuilder hostIdSearch; + private final SearchBuilder hostIdSearch; protected HostJoinDaoImpl() { @@ -97,14 +95,14 @@ public class HostJoinDaoImpl extends GenericDaoBase implements if (details.contains(HostDetails.all) || details.contains(HostDetails.capacity) || details.contains(HostDetails.stats) || details.contains(HostDetails.events)) { - hostResponse.setOsCategoryId(host.getOsCategoryUuid()); - hostResponse.setOsCategoryName(host.getOsCategoryName()); - hostResponse.setZoneName(host.getZoneName()); - hostResponse.setPodName(host.getPodName()); - if ( host.getClusterId() > 0) { - hostResponse.setClusterName(host.getClusterName()); - hostResponse.setClusterType(host.getClusterType().toString()); - } + hostResponse.setOsCategoryId(host.getOsCategoryUuid()); + hostResponse.setOsCategoryName(host.getOsCategoryName()); + hostResponse.setZoneName(host.getZoneName()); + hostResponse.setPodName(host.getPodName()); + if ( host.getClusterId() > 0) { + hostResponse.setClusterName(host.getClusterName()); + hostResponse.setClusterType(host.getClusterType().toString()); + } } DecimalFormat decimalFormat = new DecimalFormat("#.##"); diff --git a/server/src/com/cloud/api/query/dao/ProjectJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/ProjectJoinDaoImpl.java index 77f930343bf..5b2a350a56b 100644 --- a/server/src/com/cloud/api/query/dao/ProjectJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/ProjectJoinDaoImpl.java @@ -20,19 +20,17 @@ import java.util.ArrayList; import java.util.List; import javax.ejb.Local; +import javax.inject.Inject; +import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.api.ApiDBUtils; import com.cloud.api.query.vo.ProjectJoinVO; import com.cloud.api.query.vo.ResourceTagJoinVO; import com.cloud.configuration.dao.ConfigurationDao; - -import org.apache.cloudstack.api.response.ProjectResponse; -import org.springframework.stereotype.Component; - import com.cloud.projects.Project; -import com.cloud.utils.component.Inject; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -45,9 +43,9 @@ public class ProjectJoinDaoImpl extends GenericDaoBase impl @Inject private ConfigurationDao _configDao; - private SearchBuilder prjSearch; + private final SearchBuilder prjSearch; - private SearchBuilder prjIdSearch; + private final SearchBuilder prjIdSearch; protected ProjectJoinDaoImpl() { diff --git a/server/src/com/cloud/api/query/dao/ResourceTagJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/ResourceTagJoinDaoImpl.java index 2d86ca03d7c..76316577525 100644 --- a/server/src/com/cloud/api/query/dao/ResourceTagJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/ResourceTagJoinDaoImpl.java @@ -20,18 +20,16 @@ import java.util.ArrayList; import java.util.List; import javax.ejb.Local; +import javax.inject.Inject; +import org.apache.cloudstack.api.response.ResourceTagResponse; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.api.ApiResponseHelper; import com.cloud.api.query.vo.ResourceTagJoinVO; import com.cloud.configuration.dao.ConfigurationDao; - -import org.apache.cloudstack.api.response.ResourceTagResponse; -import org.springframework.stereotype.Component; - import com.cloud.server.ResourceTag; -import com.cloud.utils.component.Inject; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -44,9 +42,9 @@ public class ResourceTagJoinDaoImpl extends GenericDaoBase tagSearch; + private final SearchBuilder tagSearch; - private SearchBuilder tagIdSearch; + private final SearchBuilder tagIdSearch; protected ResourceTagJoinDaoImpl() { diff --git a/server/src/com/cloud/api/query/dao/SecurityGroupJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/SecurityGroupJoinDaoImpl.java index f6847aa9d2b..3e579c179e2 100644 --- a/server/src/com/cloud/api/query/dao/SecurityGroupJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/SecurityGroupJoinDaoImpl.java @@ -20,23 +20,21 @@ import java.util.ArrayList; import java.util.List; import javax.ejb.Local; +import javax.inject.Inject; +import org.apache.cloudstack.api.response.SecurityGroupResponse; +import org.apache.cloudstack.api.response.SecurityGroupRuleResponse; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.api.ApiDBUtils; import com.cloud.api.ApiResponseHelper; import com.cloud.api.query.vo.ResourceTagJoinVO; import com.cloud.api.query.vo.SecurityGroupJoinVO; import com.cloud.configuration.dao.ConfigurationDao; - -import org.apache.cloudstack.api.response.SecurityGroupResponse; -import org.apache.cloudstack.api.response.SecurityGroupRuleResponse; -import org.springframework.stereotype.Component; - import com.cloud.network.security.SecurityGroup; import com.cloud.network.security.SecurityRule.SecurityRuleType; import com.cloud.user.Account; -import com.cloud.utils.component.Inject; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -49,9 +47,9 @@ public class SecurityGroupJoinDaoImpl extends GenericDaoBase sgSearch; + private final SearchBuilder sgSearch; - private SearchBuilder sgIdSearch; + private final SearchBuilder sgIdSearch; protected SecurityGroupJoinDaoImpl() { diff --git a/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java index 53a0ffed445..66aecc212d8 100644 --- a/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java @@ -18,19 +18,19 @@ package com.cloud.api.query.dao; import java.util.ArrayList; import java.util.List; -import javax.ejb.Local; +import javax.ejb.Local; +import javax.inject.Inject; + +import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.api.ApiDBUtils; import com.cloud.api.query.vo.StoragePoolJoinVO; import com.cloud.configuration.dao.ConfigurationDao; -import org.apache.cloudstack.api.response.StoragePoolResponse; -import org.springframework.stereotype.Component; - import com.cloud.storage.StoragePool; import com.cloud.storage.StorageStats; -import com.cloud.utils.component.Inject; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -44,9 +44,9 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase spSearch; + private final SearchBuilder spSearch; - private SearchBuilder spIdSearch; + private final SearchBuilder spIdSearch; protected StoragePoolJoinDaoImpl() { diff --git a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index ce3d8083767..6f5587f87ea 100644 --- a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java @@ -24,23 +24,21 @@ import java.util.List; import java.util.Set; import javax.ejb.Local; - -import org.apache.log4j.Logger; - -import com.cloud.api.ApiDBUtils; -import com.cloud.api.query.vo.ResourceTagJoinVO; -import com.cloud.api.query.vo.UserVmJoinVO; -import com.cloud.configuration.dao.ConfigurationDao; +import javax.inject.Inject; import org.apache.cloudstack.api.ApiConstants.VMDetails; import org.apache.cloudstack.api.response.NicResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.log4j.Logger; import org.springframework.stereotype.Component; +import com.cloud.api.ApiDBUtils; +import com.cloud.api.query.vo.ResourceTagJoinVO; +import com.cloud.api.query.vo.UserVmJoinVO; +import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.user.Account; import com.cloud.uservm.UserVm; -import com.cloud.utils.component.Inject; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -55,7 +53,7 @@ public class UserVmJoinDaoImpl extends GenericDaoBase implem @Inject private ConfigurationDao _configDao; - private SearchBuilder VmDetailSearch; + private final SearchBuilder VmDetailSearch; protected UserVmJoinDaoImpl() { @@ -68,6 +66,7 @@ public class UserVmJoinDaoImpl extends GenericDaoBase implem } + @Override public UserVmResponse newUserVmResponse(String objectName, UserVmJoinVO userVm, EnumSet details, Account caller) { UserVmResponse userVmResponse = new UserVmResponse(); @@ -216,8 +215,9 @@ public class UserVmJoinDaoImpl extends GenericDaoBase implem userVmResponse.setObjectName(objectName); return userVmResponse; - } + } + @Override public UserVmResponse setUserVmResponse(UserVmResponse userVmData, UserVmJoinVO uvo) { Long securityGroupId = uvo.getSecurityGroupId(); if (securityGroupId != null && securityGroupId.longValue() != 0) { diff --git a/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java index 65ecd1bc57e..495c0ebc18c 100644 --- a/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java @@ -20,26 +20,24 @@ import java.util.ArrayList; import java.util.List; import javax.ejb.Local; +import javax.inject.Inject; +import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.api.ApiDBUtils; import com.cloud.api.ApiResponseHelper; import com.cloud.api.query.vo.ResourceTagJoinVO; import com.cloud.api.query.vo.VolumeJoinVO; import com.cloud.configuration.dao.ConfigurationDao; - -import org.apache.cloudstack.api.response.VolumeResponse; -import org.springframework.stereotype.Component; - import com.cloud.offering.ServiceOffering; import com.cloud.storage.Storage; import com.cloud.storage.VMTemplateHostVO; -import com.cloud.storage.Volume; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; +import com.cloud.storage.Volume; import com.cloud.user.Account; import com.cloud.user.UserContext; -import com.cloud.utils.component.Inject; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -53,9 +51,9 @@ public class VolumeJoinDaoImpl extends GenericDaoBase implem @Inject private ConfigurationDao _configDao; - private SearchBuilder volSearch; + private final SearchBuilder volSearch; - private SearchBuilder volIdSearch; + private final SearchBuilder volIdSearch; protected VolumeJoinDaoImpl() { @@ -176,12 +174,12 @@ public class VolumeJoinDaoImpl extends GenericDaoBase implem volResponse.setDestroyed(volume.getState() == Volume.State.Destroy); boolean isExtractable = true; if (volume.getVolumeType() != Volume.Type.DATADISK) { // Datadisk dont - // have any - // template - // dependence. + // have any + // template + // dependence. if (volume.getTemplateId() > 0) { // For ISO based volumes template - // = null and we allow extraction - // of all ISO based volumes + // = null and we allow extraction + // of all ISO based volumes isExtractable = volume.isExtractable() && volume.getTemplateType() != Storage.TemplateType.SYSTEM; } } diff --git a/server/src/com/cloud/async/AsyncJobExecutorContextImpl.java b/server/src/com/cloud/async/AsyncJobExecutorContextImpl.java index ed6441994eb..41814480d64 100644 --- a/server/src/com/cloud/async/AsyncJobExecutorContextImpl.java +++ b/server/src/com/cloud/async/AsyncJobExecutorContextImpl.java @@ -36,7 +36,6 @@ import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.user.AccountManager; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; -import com.cloud.utils.component.ComponentLocator; import com.cloud.vm.UserVmManager; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.dao.DomainRouterDao; @@ -45,8 +44,8 @@ import com.cloud.vm.dao.UserVmDao; @Component @Local(value={AsyncJobExecutorContext.class}) public class AsyncJobExecutorContextImpl implements AsyncJobExecutorContext { - private String _name; - + private String _name; + @Inject private AgentManager _agentMgr; @Inject private NetworkManager _networkMgr; @Inject private UserVmManager _vmMgr; @@ -62,98 +61,98 @@ public class AsyncJobExecutorContextImpl implements AsyncJobExecutorContext { @Inject private AsyncJobDao _jobDao; @Inject private UserDao _userDao; @Inject private VirtualMachineManager _itMgr; - - @Inject private ManagementServer _managementServer; - - @Override - public ManagementServer getManagementServer() { - return _managementServer; - } - @Override - public AgentManager getAgentMgr() { - return _agentMgr; - } - - @Override - public NetworkManager getNetworkMgr() { - return _networkMgr; - } - - @Override - public UserVmManager getVmMgr() { - return _vmMgr; - } - - @Override - public StorageManager getStorageMgr() { - return _storageMgr; - } - - /**server/src/com/cloud/async/AsyncJobExecutorContext.java + @Inject private ManagementServer _managementServer; + + @Override + public ManagementServer getManagementServer() { + return _managementServer; + } + + @Override + public AgentManager getAgentMgr() { + return _agentMgr; + } + + @Override + public NetworkManager getNetworkMgr() { + return _networkMgr; + } + + @Override + public UserVmManager getVmMgr() { + return _vmMgr; + } + + @Override + public StorageManager getStorageMgr() { + return _storageMgr; + } + + /**server/src/com/cloud/async/AsyncJobExecutorContext.java * @return the _snapMgr */ - @Override + @Override public SnapshotManager getSnapshotMgr() { return _snapMgr; } @Override - public AccountManager getAccountMgr() { - return _accountMgr; - } - - @Override - public EventDao getEventDao() { - return _eventDao; - } - - @Override - public UserVmDao getVmDao() { - return _vmDao; - } - - @Override - public AccountDao getAccountDao() { - return _accountDao; - } - - @Override - public VolumeDao getVolumeDao() { - return _volumeDao; - } + public AccountManager getAccountMgr() { + return _accountMgr; + } - @Override + @Override + public EventDao getEventDao() { + return _eventDao; + } + + @Override + public UserVmDao getVmDao() { + return _vmDao; + } + + @Override + public AccountDao getAccountDao() { + return _accountDao; + } + + @Override + public VolumeDao getVolumeDao() { + return _volumeDao; + } + + @Override public DomainRouterDao getRouterDao() { - return _routerDao; - } - - @Override + return _routerDao; + } + + @Override public IPAddressDao getIpAddressDao() { - return _ipAddressDao; + return _ipAddressDao; } - - @Override + + @Override public AsyncJobDao getJobDao() { - return _jobDao; + return _jobDao; } - - @Override + + @Override public UserDao getUserDao() { - return _userDao; + return _userDao; } - - @Override - public VirtualMachineManager getItMgr() { - return _itMgr; - } - + + @Override + public VirtualMachineManager getItMgr() { + return _itMgr; + } + @Override public boolean configure(String name, Map params) throws ConfigurationException { - _name = name; - return true; + _name = name; + return true; } - + @Override public boolean start() { return true; @@ -163,9 +162,9 @@ public class AsyncJobExecutorContextImpl implements AsyncJobExecutorContext { public boolean stop() { return true; } - + @Override public String getName() { - return _name; + return _name; } } diff --git a/server/src/com/cloud/async/AsyncJobManagerImpl.java b/server/src/com/cloud/async/AsyncJobManagerImpl.java index 25b7e6086fd..1446236445a 100644 --- a/server/src/com/cloud/async/AsyncJobManagerImpl.java +++ b/server/src/com/cloud/async/AsyncJobManagerImpl.java @@ -35,7 +35,11 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; +import org.apache.cloudstack.api.response.ExceptionResponse; import org.apache.log4j.Logger; import org.apache.log4j.NDC; import org.springframework.stereotype.Component; @@ -43,15 +47,10 @@ import org.springframework.stereotype.Component; import com.cloud.api.ApiDispatcher; import com.cloud.api.ApiGsonHelper; import com.cloud.api.ApiSerializerHelper; -import org.apache.cloudstack.api.BaseAsyncCmd; -import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.ExceptionResponse; import com.cloud.async.dao.AsyncJobDao; import com.cloud.cluster.ClusterManager; import com.cloud.cluster.ClusterManagerListener; import com.cloud.cluster.ManagementServerHostVO; -import com.cloud.cluster.StackMaid; import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.exception.InvalidParameterValueException; @@ -64,7 +63,6 @@ import com.cloud.user.dao.AccountDao; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.db.GlobalLock; @@ -80,14 +78,14 @@ import com.google.gson.reflect.TypeToken; @Local(value={AsyncJobManager.class}) public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListener { public static final Logger s_logger = Logger.getLogger(AsyncJobManagerImpl.class.getName()); - private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 3; // 3 seconds - + private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 3; // 3 seconds + private static final int MAX_ONETIME_SCHEDULE_SIZE = 50; private static final int HEARTBEAT_INTERVAL = 2000; private static final int GC_INTERVAL = 10000; // 10 seconds - + private String _name; - + @Inject private AsyncJobExecutorContext _context; @Inject private SyncQueueManager _queueMgr; @Inject private ClusterManager _clusterMgr; @@ -97,201 +95,201 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe @Inject private ConfigurationDao _configDao; private long _jobExpireSeconds = 86400; // 1 day private long _jobCancelThresholdSeconds = 3600; // 1 hour (for cancelling the jobs blocking other jobs) - - private ApiDispatcher _dispatcher; + + @Inject private ApiDispatcher _dispatcher; private final ScheduledExecutorService _heartbeatScheduler = - Executors.newScheduledThreadPool(1, new NamedThreadFactory("AsyncJobMgr-Heartbeat")); + Executors.newScheduledThreadPool(1, new NamedThreadFactory("AsyncJobMgr-Heartbeat")); private ExecutorService _executor; @Override - public AsyncJobExecutorContext getExecutorContext() { - return _context; - } - - @Override - public AsyncJobVO getAsyncJob(long jobId) { - return _jobDao.findById(jobId); + public AsyncJobExecutorContext getExecutorContext() { + return _context; } - + @Override - public AsyncJobVO findInstancePendingAsyncJob(String instanceType, long instanceId) { - return _jobDao.findInstancePendingAsyncJob(instanceType, instanceId); + public AsyncJobVO getAsyncJob(long jobId) { + return _jobDao.findById(jobId); + } + + @Override + public AsyncJobVO findInstancePendingAsyncJob(String instanceType, long instanceId) { + return _jobDao.findInstancePendingAsyncJob(instanceType, instanceId); } - + @Override public List findInstancePendingAsyncJobs(AsyncJob.Type instanceType, Long accountId) { - return _jobDao.findInstancePendingAsyncJobs(instanceType, accountId); + return _jobDao.findInstancePendingAsyncJobs(instanceType, accountId); } - + @Override - public long submitAsyncJob(AsyncJobVO job) { - return submitAsyncJob(job, false); + public long submitAsyncJob(AsyncJobVO job) { + return submitAsyncJob(job, false); } @Override @DB public long submitAsyncJob(AsyncJobVO job, boolean scheduleJobExecutionInContext) { - Transaction txt = Transaction.currentTxn(); - try { - txt.start(); - job.setInitMsid(getMsid()); - _jobDao.persist(job); - txt.commit(); + Transaction txt = Transaction.currentTxn(); + try { + txt.start(); + job.setInitMsid(getMsid()); + _jobDao.persist(job); + txt.commit(); - // no sync source originally - job.setSyncSource(null); - scheduleExecution(job, scheduleJobExecutionInContext); - if(s_logger.isDebugEnabled()) { + // no sync source originally + job.setSyncSource(null); + scheduleExecution(job, scheduleJobExecutionInContext); + if(s_logger.isDebugEnabled()) { s_logger.debug("submit async job-" + job.getId() + ", details: " + job.toString()); } - return job.getId(); - } catch(Exception e) { - txt.rollback(); - String errMsg = "Unable to schedule async job for command " + job.getCmd() + ", unexpected exception."; + return job.getId(); + } catch(Exception e) { + txt.rollback(); + String errMsg = "Unable to schedule async job for command " + job.getCmd() + ", unexpected exception."; s_logger.warn(errMsg, e); throw new CloudRuntimeException(errMsg); - } + } } @Override @DB public void completeAsyncJob(long jobId, int jobStatus, int resultCode, Object resultObject) { - if(s_logger.isDebugEnabled()) { + if(s_logger.isDebugEnabled()) { s_logger.debug("Complete async job-" + jobId + ", jobStatus: " + jobStatus + - ", resultCode: " + resultCode + ", result: " + resultObject); + ", resultCode: " + resultCode + ", result: " + resultObject); } - - Transaction txt = Transaction.currentTxn(); - try { - txt.start(); - AsyncJobVO job = _jobDao.findById(jobId); - if(job == null) { - if(s_logger.isDebugEnabled()) { + + Transaction txt = Transaction.currentTxn(); + try { + txt.start(); + AsyncJobVO job = _jobDao.findById(jobId); + if(job == null) { + if(s_logger.isDebugEnabled()) { s_logger.debug("job-" + jobId + " no longer exists, we just log completion info here. " + jobStatus + - ", resultCode: " + resultCode + ", result: " + resultObject); + ", resultCode: " + resultCode + ", result: " + resultObject); } - - txt.rollback(); - return; - } + + txt.rollback(); + return; + } - job.setCompleteMsid(getMsid()); - job.setStatus(jobStatus); - job.setResultCode(resultCode); + job.setCompleteMsid(getMsid()); + job.setStatus(jobStatus); + job.setResultCode(resultCode); - // reset attached object - job.setInstanceType(null); - job.setInstanceId(null); + // reset attached object + job.setInstanceType(null); + job.setInstanceId(null); - if (resultObject != null) { + if (resultObject != null) { job.setResult(ApiSerializerHelper.toSerializedStringOld(resultObject)); - } + } - job.setLastUpdated(DateUtil.currentGMTTime()); - _jobDao.update(jobId, job); - txt.commit(); - } catch(Exception e) { - s_logger.error("Unexpected exception while completing async job-" + jobId, e); - txt.rollback(); - } + job.setLastUpdated(DateUtil.currentGMTTime()); + _jobDao.update(jobId, job); + txt.commit(); + } catch(Exception e) { + s_logger.error("Unexpected exception while completing async job-" + jobId, e); + txt.rollback(); + } } @Override @DB public void updateAsyncJobStatus(long jobId, int processStatus, Object resultObject) { - if(s_logger.isDebugEnabled()) { + if(s_logger.isDebugEnabled()) { s_logger.debug("Update async-job progress, job-" + jobId + ", processStatus: " + processStatus + - ", result: " + resultObject); + ", result: " + resultObject); } - - Transaction txt = Transaction.currentTxn(); - try { - txt.start(); - AsyncJobVO job = _jobDao.findById(jobId); - if(job == null) { - if(s_logger.isDebugEnabled()) { + + Transaction txt = Transaction.currentTxn(); + try { + txt.start(); + AsyncJobVO job = _jobDao.findById(jobId); + if(job == null) { + if(s_logger.isDebugEnabled()) { s_logger.debug("job-" + jobId + " no longer exists, we just log progress info here. progress status: " + processStatus); } - - txt.rollback(); - return; - } - - job.setProcessStatus(processStatus); - if(resultObject != null) { + + txt.rollback(); + return; + } + + job.setProcessStatus(processStatus); + if(resultObject != null) { job.setResult(ApiSerializerHelper.toSerializedStringOld(resultObject)); } - job.setLastUpdated(DateUtil.currentGMTTime()); - _jobDao.update(jobId, job); - txt.commit(); - } catch(Exception e) { - s_logger.error("Unexpected exception while updating async job-" + jobId + " status: ", e); - txt.rollback(); - } + job.setLastUpdated(DateUtil.currentGMTTime()); + _jobDao.update(jobId, job); + txt.commit(); + } catch(Exception e) { + s_logger.error("Unexpected exception while updating async job-" + jobId + " status: ", e); + txt.rollback(); + } } @Override @DB public void updateAsyncJobAttachment(long jobId, String instanceType, Long instanceId) { - if(s_logger.isDebugEnabled()) { + if(s_logger.isDebugEnabled()) { s_logger.debug("Update async-job attachment, job-" + jobId + ", instanceType: " + instanceType + - ", instanceId: " + instanceId); + ", instanceId: " + instanceId); } - - Transaction txt = Transaction.currentTxn(); - try { - txt.start(); + + Transaction txt = Transaction.currentTxn(); + try { + txt.start(); - AsyncJobVO job = _jobDao.createForUpdate(); - //job.setInstanceType(instanceType); - job.setInstanceId(instanceId); - job.setLastUpdated(DateUtil.currentGMTTime()); - _jobDao.update(jobId, job); + AsyncJobVO job = _jobDao.createForUpdate(); + //job.setInstanceType(instanceType); + job.setInstanceId(instanceId); + job.setLastUpdated(DateUtil.currentGMTTime()); + _jobDao.update(jobId, job); - txt.commit(); - } catch(Exception e) { - s_logger.error("Unexpected exception while updating async job-" + jobId + " attachment: ", e); - txt.rollback(); - } + txt.commit(); + } catch(Exception e) { + s_logger.error("Unexpected exception while updating async job-" + jobId + " attachment: ", e); + txt.rollback(); + } } @Override public void syncAsyncJobExecution(AsyncJob job, String syncObjType, long syncObjId, long queueSizeLimit) { - // This method is re-entrant. If an API developer wants to synchronized on an object, e.g. the router, - // when executing business logic, they will call this method (actually a method in BaseAsyncCmd that calls this). - // This method will get called every time their business logic executes. The first time it exectues for a job - // there will be no sync source, but on subsequent execution there will be a sync souce. If this is the first - // time the job executes we queue the job, otherwise we just return so that the business logic can execute. + // This method is re-entrant. If an API developer wants to synchronized on an object, e.g. the router, + // when executing business logic, they will call this method (actually a method in BaseAsyncCmd that calls this). + // This method will get called every time their business logic executes. The first time it exectues for a job + // there will be no sync source, but on subsequent execution there will be a sync souce. If this is the first + // time the job executes we queue the job, otherwise we just return so that the business logic can execute. if (job.getSyncSource() != null) { return; } - + if(s_logger.isDebugEnabled()) { s_logger.debug("Sync job-" + job.getId() + " execution on object " + syncObjType + "." + syncObjId); } - SyncQueueVO queue = null; + SyncQueueVO queue = null; - // to deal with temporary DB exceptions like DB deadlock/Lock-wait time out cased rollbacks - // we retry five times until we throw an exception - Random random = new Random(); + // to deal with temporary DB exceptions like DB deadlock/Lock-wait time out cased rollbacks + // we retry five times until we throw an exception + Random random = new Random(); - for(int i = 0; i < 5; i++) { + for(int i = 0; i < 5; i++) { queue = _queueMgr.queue(syncObjType, syncObjId, SyncQueueItem.AsyncJobContentType, job.getId(), queueSizeLimit); - if(queue != null) { + if(queue != null) { break; } - try { - Thread.sleep(1000 + random.nextInt(5000)); - } catch (InterruptedException e) { - } - } + try { + Thread.sleep(1000 + random.nextInt(5000)); + } catch (InterruptedException e) { + } + } - if (queue == null) { + if (queue == null) { throw new CloudRuntimeException("Unable to insert queue item into database, DB is full?"); - } else { - throw new AsyncCommandQueued(queue, "job-" + job.getId() + " queued"); - } + } else { + throw new AsyncCommandQueued(queue, "job-" + job.getId() + " queued"); + } } - + @Override public AsyncJob queryAsyncJobResult(QueryAsyncJobResultCmd cmd) { Account caller = UserContext.current().getCaller(); @@ -300,10 +298,10 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe if (job == null) { throw new InvalidParameterValueException("Unable to find a job by id " + cmd.getId()); } - + User userJobOwner = _accountMgr.getUserIncludingRemoved(job.getUserId()); Account jobOwner = _accountMgr.getAccount(userJobOwner.getAccountId()); - + //check permissions if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL) { //regular user can see only jobs he owns @@ -313,7 +311,7 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe } else if (caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) { _accountMgr.checkAccess(caller, null, true, jobOwner); } - + //poll the job queryAsyncJobResult(cmd.getId()); return _jobDao.findById(cmd.getId()); @@ -321,56 +319,56 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe @Override @DB public AsyncJobResult queryAsyncJobResult(long jobId) { - if(s_logger.isTraceEnabled()) { + if(s_logger.isTraceEnabled()) { s_logger.trace("Query async-job status, job-" + jobId); } - - Transaction txt = Transaction.currentTxn(); - AsyncJobResult jobResult = new AsyncJobResult(jobId); - - try { - txt.start(); - AsyncJobVO job = _jobDao.findById(jobId); - if(job != null) { - jobResult.setCmdOriginator(job.getCmdOriginator()); - jobResult.setJobStatus(job.getStatus()); - jobResult.setProcessStatus(job.getProcessStatus()); - jobResult.setResult(job.getResult()); - jobResult.setResultCode(job.getResultCode()); - jobResult.setUuid(job.getUuid()); - - if(job.getStatus() == AsyncJobResult.STATUS_SUCCEEDED || - job.getStatus() == AsyncJobResult.STATUS_FAILED) { - - if(s_logger.isDebugEnabled()) { + + Transaction txt = Transaction.currentTxn(); + AsyncJobResult jobResult = new AsyncJobResult(jobId); + + try { + txt.start(); + AsyncJobVO job = _jobDao.findById(jobId); + if(job != null) { + jobResult.setCmdOriginator(job.getCmdOriginator()); + jobResult.setJobStatus(job.getStatus()); + jobResult.setProcessStatus(job.getProcessStatus()); + jobResult.setResult(job.getResult()); + jobResult.setResultCode(job.getResultCode()); + jobResult.setUuid(job.getUuid()); + + if(job.getStatus() == AsyncJobResult.STATUS_SUCCEEDED || + job.getStatus() == AsyncJobResult.STATUS_FAILED) { + + if(s_logger.isDebugEnabled()) { s_logger.debug("Async job-" + jobId + " completed"); } - } else { - job.setLastPolled(DateUtil.currentGMTTime()); - _jobDao.update(jobId, job); - } - } else { - if(s_logger.isDebugEnabled()) { + } else { + job.setLastPolled(DateUtil.currentGMTTime()); + _jobDao.update(jobId, job); + } + } else { + if(s_logger.isDebugEnabled()) { s_logger.debug("Async job-" + jobId + " does not exist, invalid job id?"); } - - jobResult.setJobStatus(AsyncJobResult.STATUS_FAILED); - jobResult.setResult("job-" + jobId + " does not exist"); - } - txt.commit(); - } catch(Exception e) { - s_logger.error("Unexpected exception while querying async job-" + jobId + " status: ", e); - - jobResult.setJobStatus(AsyncJobResult.STATUS_FAILED); - jobResult.setResult("Exception: " + e.toString()); - txt.rollback(); - } - - if(s_logger.isTraceEnabled()) { + + jobResult.setJobStatus(AsyncJobResult.STATUS_FAILED); + jobResult.setResult("job-" + jobId + " does not exist"); + } + txt.commit(); + } catch(Exception e) { + s_logger.error("Unexpected exception while querying async job-" + jobId + " status: ", e); + + jobResult.setJobStatus(AsyncJobResult.STATUS_FAILED); + jobResult.setResult("Exception: " + e.toString()); + txt.rollback(); + } + + if(s_logger.isTraceEnabled()) { s_logger.trace("Job status: " + jobResult.toString()); } - - return jobResult; + + return jobResult; } private void scheduleExecution(final AsyncJobVO job) { @@ -382,7 +380,7 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe if (executeInContext) { runnable.run(); } else { - _executor.submit(runnable); + _executor.submit(runnable); } } @@ -392,66 +390,66 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe public void run() { try { long jobId = 0; - + try { - JmxUtil.registerMBean("AsyncJobManager", "Active Job " + job.getId(), new AsyncJobMBeanImpl(job)); + JmxUtil.registerMBean("AsyncJobManager", "Active Job " + job.getId(), new AsyncJobMBeanImpl(job)); } catch(Exception e) { - s_logger.warn("Unable to register active job " + job.getId() + " to JMX monitoring due to exception " + ExceptionUtil.toString(e)); + s_logger.warn("Unable to register active job " + job.getId() + " to JMX monitoring due to exception " + ExceptionUtil.toString(e)); } - + BaseAsyncCmd cmdObj = null; Transaction txn = Transaction.open(Transaction.CLOUD_DB); try { jobId = job.getId(); NDC.push("job-" + jobId); - + if(s_logger.isDebugEnabled()) { s_logger.debug("Executing " + job.getCmd() + " for job-" + jobId); } - + Class cmdClass = Class.forName(job.getCmd()); cmdObj = (BaseAsyncCmd)cmdClass.newInstance(); cmdObj.setJob(job); - + Type mapType = new TypeToken>() {}.getType(); Gson gson = ApiGsonHelper.getBuilder().create(); Map params = gson.fromJson(job.getCmdInfo(), mapType); - + // whenever we deserialize, the UserContext needs to be updated String userIdStr = params.get("ctxUserId"); String acctIdStr = params.get("ctxAccountId"); Long userId = null; Account accountObject = null; - + if (userIdStr != null) { userId = Long.parseLong(userIdStr); } - + if (acctIdStr != null) { accountObject = _accountDao.findById(Long.parseLong(acctIdStr)); } - + UserContext.registerContext(userId, accountObject, null, false); try { // dispatch could ultimately queue the job _dispatcher.dispatch(cmdObj, params); - + // serialize this to the async job table completeAsyncJob(jobId, AsyncJobResult.STATUS_SUCCEEDED, 0, cmdObj.getResponseObject()); } finally { UserContext.unregisterContext(); } - + // commands might need to be queued as part of synchronization here, so they just have to be re-dispatched from the queue mechanism... if (job.getSyncSource() != null) { _queueMgr.purgeItem(job.getSyncSource().getId()); checkQueue(job.getSyncSource().getQueueId()); } - + if (s_logger.isDebugEnabled()) { s_logger.debug("Done executing " + job.getCmd() + " for job-" + jobId); } - + } catch(Throwable e) { if (e instanceof AsyncCommandQueued) { if (s_logger.isDebugEnabled()) { @@ -469,16 +467,16 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe errorMsg = sApiEx.getDescription(); errorCode = sApiEx.getErrorCode(); } - + ExceptionResponse response = new ExceptionResponse(); response.setErrorCode(errorCode); response.setErrorText(errorMsg); response.setResponseName((cmdObj == null) ? "unknowncommandresponse" : cmdObj.getCommandName()); - + // FIXME: setting resultCode to BaseCmd.INTERNAL_ERROR is not right, usually executors have their exception handling // and we need to preserve that as much as possible here completeAsyncJob(jobId, AsyncJobResult.STATUS_FAILED, BaseCmd.INTERNAL_ERROR, response); - + // need to clean up any queue that happened as part of the dispatching and move on to the next item in the queue try { if (job.getSyncSource() != null) { @@ -490,14 +488,13 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe } } } finally { - + try { - JmxUtil.unregisterMBean("AsyncJobManager", "Active Job " + job.getId()); + JmxUtil.unregisterMBean("AsyncJobManager", "Active Job " + job.getId()); } catch(Exception e) { - s_logger.warn("Unable to unregister active job " + job.getId() + " from JMX monitoring"); + s_logger.warn("Unable to unregister active job " + job.getId() + " from JMX monitoring"); } - - StackMaid.current().exitCleanup(); + txn.close(); NDC.pop(); } @@ -520,17 +517,17 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe job.setFromPreviousSession(fromPreviousSession); job.setSyncSource(item); - + job.setCompleteMsid(getMsid()); _jobDao.update(job.getId(), job); - + try { - scheduleExecution(job); - } catch(RejectedExecutionException e) { - s_logger.warn("Execution for job-" + job.getId() + " is rejected, return it to the queue for next turn"); - _queueMgr.returnItem(item.getId()); - } - + scheduleExecution(job); + } catch(RejectedExecutionException e) { + s_logger.warn("Execution for job-" + job.getId() + " is rejected, return it to the queue for next turn"); + _queueMgr.returnItem(item.getId()); + } + } else { if(s_logger.isDebugEnabled()) { s_logger.debug("Unable to find related job for queue item: " + item.toString()); @@ -542,121 +539,117 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe @Override public void releaseSyncSource(AsyncJobExecutor executor) { - if(executor.getSyncSource() != null) { - if(s_logger.isDebugEnabled()) { + if(executor.getSyncSource() != null) { + if(s_logger.isDebugEnabled()) { s_logger.debug("Release sync source for job-" + executor.getJob().getId() + " sync source: " - + executor.getSyncSource().getContentType() + "-" - + executor.getSyncSource().getContentId()); + + executor.getSyncSource().getContentType() + "-" + + executor.getSyncSource().getContentId()); } - - _queueMgr.purgeItem(executor.getSyncSource().getId()); - checkQueue(executor.getSyncSource().getQueueId()); - } + + _queueMgr.purgeItem(executor.getSyncSource().getId()); + checkQueue(executor.getSyncSource().getQueueId()); + } } - + private void checkQueue(long queueId) { - while(true) { - try { - SyncQueueItemVO item = _queueMgr.dequeueFromOne(queueId, getMsid()); - if(item != null) { - if(s_logger.isDebugEnabled()) { + while(true) { + try { + SyncQueueItemVO item = _queueMgr.dequeueFromOne(queueId, getMsid()); + if(item != null) { + if(s_logger.isDebugEnabled()) { s_logger.debug("Executing sync queue item: " + item.toString()); } - - executeQueueItem(item, false); - } else { - break; - } - } catch(Throwable e) { - s_logger.error("Unexpected exception when kicking sync queue-" + queueId, e); - break; - } - } + + executeQueueItem(item, false); + } else { + break; + } + } catch(Throwable e) { + s_logger.error("Unexpected exception when kicking sync queue-" + queueId, e); + break; + } + } } - - private Runnable getHeartbeatTask() { - return new Runnable() { - @Override + + private Runnable getHeartbeatTask() { + return new Runnable() { + @Override public void run() { - try { - List l = _queueMgr.dequeueFromAny(getMsid(), MAX_ONETIME_SCHEDULE_SIZE); - if(l != null && l.size() > 0) { - for(SyncQueueItemVO item: l) { - if(s_logger.isDebugEnabled()) { + try { + List l = _queueMgr.dequeueFromAny(getMsid(), MAX_ONETIME_SCHEDULE_SIZE); + if(l != null && l.size() > 0) { + for(SyncQueueItemVO item: l) { + if(s_logger.isDebugEnabled()) { s_logger.debug("Execute sync-queue item: " + item.toString()); } - executeQueueItem(item, false); - } - } - } catch(Throwable e) { - s_logger.error("Unexpected exception when trying to execute queue item, ", e); - } finally { - StackMaid.current().exitCleanup(); - } - } - }; - } - - @DB - private Runnable getGCTask() { - return new Runnable() { - @Override + executeQueueItem(item, false); + } + } + } catch(Throwable e) { + s_logger.error("Unexpected exception when trying to execute queue item, ", e); + } + } + }; + } + + @DB + private Runnable getGCTask() { + return new Runnable() { + @Override public void run() { - GlobalLock scanLock = GlobalLock.getInternLock("AsyncJobManagerGC"); - try { - if(scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) { - try { - reallyRun(); - } finally { - scanLock.unlock(); - } - } - } finally { - scanLock.releaseRef(); - } - } - + GlobalLock scanLock = GlobalLock.getInternLock("AsyncJobManagerGC"); + try { + if(scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) { + try { + reallyRun(); + } finally { + scanLock.unlock(); + } + } + } finally { + scanLock.releaseRef(); + } + } + public void reallyRun() { - try { - s_logger.trace("Begin cleanup expired async-jobs"); - - Date cutTime = new Date(DateUtil.currentGMTTime().getTime() - _jobExpireSeconds*1000); - - // limit to 100 jobs per turn, this gives cleanup throughput as 600 jobs per minute - // hopefully this will be fast enough to balance potential growth of job table - List l = _jobDao.getExpiredJobs(cutTime, 100); - if(l != null && l.size() > 0) { - for(AsyncJobVO job : l) { + try { + s_logger.trace("Begin cleanup expired async-jobs"); + + Date cutTime = new Date(DateUtil.currentGMTTime().getTime() - _jobExpireSeconds*1000); + + // limit to 100 jobs per turn, this gives cleanup throughput as 600 jobs per minute + // hopefully this will be fast enough to balance potential growth of job table + List l = _jobDao.getExpiredJobs(cutTime, 100); + if(l != null && l.size() > 0) { + for(AsyncJobVO job : l) { expungeAsyncJob(job); - } - } - + } + } + // forcefully cancel blocking queue items if they've been staying there for too long - List blockItems = _queueMgr.getBlockedQueueItems(_jobCancelThresholdSeconds*1000, false); - if(blockItems != null && blockItems.size() > 0) { - for(SyncQueueItemVO item : blockItems) { + List blockItems = _queueMgr.getBlockedQueueItems(_jobCancelThresholdSeconds*1000, false); + if(blockItems != null && blockItems.size() > 0) { + for(SyncQueueItemVO item : blockItems) { if(item.getContentType().equalsIgnoreCase(SyncQueueItem.AsyncJobContentType)) { completeAsyncJob(item.getContentId(), AsyncJobResult.STATUS_FAILED, 0, getResetResultResponse("Job is cancelled as it has been blocking others for too long")); } - - // purge the item and resume queue processing - _queueMgr.purgeItem(item.getId()); - } - } - - s_logger.trace("End cleanup expired async-jobs"); - } catch(Throwable e) { - s_logger.error("Unexpected exception when trying to execute queue item, ", e); - } finally { - StackMaid.current().exitCleanup(); - } - } - - }; - } - + // purge the item and resume queue processing + _queueMgr.purgeItem(item.getId()); + } + } + + s_logger.trace("End cleanup expired async-jobs"); + } catch(Throwable e) { + s_logger.error("Unexpected exception when trying to execute queue item, ", e); + } + } + + + }; + } + @DB protected void expungeAsyncJob(AsyncJobVO job) { Transaction txn = Transaction.currentTxn(); @@ -667,132 +660,129 @@ public class AsyncJobManagerImpl implements AsyncJobManager, ClusterManagerListe txn.commit(); } - private long getMsid() { - if(_clusterMgr != null) { + private long getMsid() { + if(_clusterMgr != null) { return _clusterMgr.getManagementNodeId(); } - - return MacAddress.getMacAddress().toLong(); - } - - private void cleanupPendingJobs(List l) { - if(l != null && l.size() > 0) { - for(SyncQueueItemVO item: l) { - if(s_logger.isInfoEnabled()) { + + return MacAddress.getMacAddress().toLong(); + } + + private void cleanupPendingJobs(List l) { + if(l != null && l.size() > 0) { + for(SyncQueueItemVO item: l) { + if(s_logger.isInfoEnabled()) { s_logger.info("Discard left-over queue item: " + item.toString()); } - - String contentType = item.getContentType(); + + String contentType = item.getContentType(); if(contentType != null && contentType.equalsIgnoreCase(SyncQueueItem.AsyncJobContentType)) { - Long jobId = item.getContentId(); - if(jobId != null) { - s_logger.warn("Mark job as failed as its correspoding queue-item has been discarded. job id: " + jobId); - completeAsyncJob(jobId, AsyncJobResult.STATUS_FAILED, 0, getResetResultResponse("Execution was cancelled because of server shutdown")); - } - } - _queueMgr.purgeItem(item.getId()); - } - } - } - + Long jobId = item.getContentId(); + if(jobId != null) { + s_logger.warn("Mark job as failed as its correspoding queue-item has been discarded. job id: " + jobId); + completeAsyncJob(jobId, AsyncJobResult.STATUS_FAILED, 0, getResetResultResponse("Execution was cancelled because of server shutdown")); + } + } + _queueMgr.purgeItem(item.getId()); + } + } + } + @Override public boolean configure(String name, Map params) throws ConfigurationException { - _name = name; - - int expireMinutes = NumbersUtil.parseInt( - _configDao.getValue(Config.JobExpireMinutes.key()), 24*60); - _jobExpireSeconds = (long)expireMinutes*60; - - _jobCancelThresholdSeconds = NumbersUtil.parseInt( - _configDao.getValue(Config.JobCancelThresholdMinutes.key()), 60); - _jobCancelThresholdSeconds *= 60; + _name = name; - _dispatcher = ApiDispatcher.getInstance(); - + int expireMinutes = NumbersUtil.parseInt( + _configDao.getValue(Config.JobExpireMinutes.key()), 24*60); + _jobExpireSeconds = (long)expireMinutes*60; + + _jobCancelThresholdSeconds = NumbersUtil.parseInt( + _configDao.getValue(Config.JobCancelThresholdMinutes.key()), 60); + _jobCancelThresholdSeconds *= 60; + + try { + final File dbPropsFile = PropertiesUtil.findConfigFile("db.properties"); + final Properties dbProps = new Properties(); + dbProps.load(new FileInputStream(dbPropsFile)); - try { - final File dbPropsFile = PropertiesUtil.findConfigFile("db.properties"); - final Properties dbProps = new Properties(); - dbProps.load(new FileInputStream(dbPropsFile)); - final int cloudMaxActive = Integer.parseInt(dbProps.getProperty("db.cloud.maxActive")); - + int poolSize = (cloudMaxActive * 2) / 3; - + s_logger.info("Start AsyncJobManager thread pool in size " + poolSize); _executor = Executors.newFixedThreadPool(poolSize, new NamedThreadFactory("Job-Executor")); - } catch (final Exception e) { - throw new ConfigurationException("Unable to load db.properties to configure AsyncJobManagerImpl"); - } - - return true; + } catch (final Exception e) { + throw new ConfigurationException("Unable to load db.properties to configure AsyncJobManagerImpl"); + } + + return true; } - + @Override - public void onManagementNodeJoined(List nodeList, long selfNodeId) { + public void onManagementNodeJoined(List nodeList, long selfNodeId) { } - + @Override - public void onManagementNodeLeft(List nodeList, long selfNodeId) { - for(ManagementServerHostVO msHost : nodeList) { - Transaction txn = Transaction.open(Transaction.CLOUD_DB); - try { - txn.start(); - List items = _queueMgr.getActiveQueueItems(msHost.getId(), true); - cleanupPendingJobs(items); - _jobDao.resetJobProcess(msHost.getId(), BaseCmd.INTERNAL_ERROR, getSerializedErrorMessage("job cancelled because of management server restart")); - txn.commit(); - } catch(Throwable e) { - s_logger.warn("Unexpected exception ", e); - txn.rollback(); - } finally { - txn.close(); - } - } + public void onManagementNodeLeft(List nodeList, long selfNodeId) { + for(ManagementServerHostVO msHost : nodeList) { + Transaction txn = Transaction.open(Transaction.CLOUD_DB); + try { + txn.start(); + List items = _queueMgr.getActiveQueueItems(msHost.getId(), true); + cleanupPendingJobs(items); + _jobDao.resetJobProcess(msHost.getId(), BaseCmd.INTERNAL_ERROR, getSerializedErrorMessage("job cancelled because of management server restart")); + txn.commit(); + } catch(Throwable e) { + s_logger.warn("Unexpected exception ", e); + txn.rollback(); + } finally { + txn.close(); + } + } } - + @Override - public void onManagementNodeIsolated() { - } + public void onManagementNodeIsolated() { + } @Override public boolean start() { - try { - List l = _queueMgr.getActiveQueueItems(getMsid(), false); - cleanupPendingJobs(l); - _jobDao.resetJobProcess(getMsid(), BaseCmd.INTERNAL_ERROR, getSerializedErrorMessage("job cancelled because of management server restart")); - } catch(Throwable e) { - s_logger.error("Unexpected exception " + e.getMessage(), e); - } - - _heartbeatScheduler.scheduleAtFixedRate(getHeartbeatTask(), HEARTBEAT_INTERVAL, - HEARTBEAT_INTERVAL, TimeUnit.MILLISECONDS); - _heartbeatScheduler.scheduleAtFixedRate(getGCTask(), GC_INTERVAL, - GC_INTERVAL, TimeUnit.MILLISECONDS); - + try { + List l = _queueMgr.getActiveQueueItems(getMsid(), false); + cleanupPendingJobs(l); + _jobDao.resetJobProcess(getMsid(), BaseCmd.INTERNAL_ERROR, getSerializedErrorMessage("job cancelled because of management server restart")); + } catch(Throwable e) { + s_logger.error("Unexpected exception " + e.getMessage(), e); + } + + _heartbeatScheduler.scheduleAtFixedRate(getHeartbeatTask(), HEARTBEAT_INTERVAL, + HEARTBEAT_INTERVAL, TimeUnit.MILLISECONDS); + _heartbeatScheduler.scheduleAtFixedRate(getGCTask(), GC_INTERVAL, + GC_INTERVAL, TimeUnit.MILLISECONDS); + return true; } - + private static ExceptionResponse getResetResultResponse(String errorMessage) { - ExceptionResponse resultObject = new ExceptionResponse(); - resultObject.setErrorCode(BaseCmd.INTERNAL_ERROR); - resultObject.setErrorText(errorMessage); - return resultObject; + ExceptionResponse resultObject = new ExceptionResponse(); + resultObject.setErrorCode(BaseCmd.INTERNAL_ERROR); + resultObject.setErrorText(errorMessage); + return resultObject; } - + private static String getSerializedErrorMessage(String errorMessage) { return ApiSerializerHelper.toSerializedStringOld(getResetResultResponse(errorMessage)); } @Override public boolean stop() { - _heartbeatScheduler.shutdown(); - _executor.shutdown(); + _heartbeatScheduler.shutdown(); + _executor.shutdown(); return true; } - + @Override public String getName() { - return _name; + return _name; } } diff --git a/server/src/com/cloud/async/SyncQueueManagerImpl.java b/server/src/com/cloud/async/SyncQueueManagerImpl.java index 97ce8a6ccad..4f0daa00bfa 100644 --- a/server/src/com/cloud/async/SyncQueueManagerImpl.java +++ b/server/src/com/cloud/async/SyncQueueManagerImpl.java @@ -31,7 +31,6 @@ import org.springframework.stereotype.Component; import com.cloud.async.dao.SyncQueueDao; import com.cloud.async.dao.SyncQueueItemDao; import com.cloud.utils.DateUtil; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; @@ -40,9 +39,9 @@ import com.cloud.utils.exception.CloudRuntimeException; @Local(value={SyncQueueManager.class}) public class SyncQueueManagerImpl implements SyncQueueManager { public static final Logger s_logger = Logger.getLogger(SyncQueueManagerImpl.class.getName()); - + private String _name; - + @Inject private SyncQueueDao _syncQueueDao; @Inject private SyncQueueItemDao _syncQueueItemDao; @@ -50,203 +49,203 @@ public class SyncQueueManagerImpl implements SyncQueueManager { @DB public SyncQueueVO queue(String syncObjType, long syncObjId, String itemType, long itemId, long queueSizeLimit) { Transaction txn = Transaction.currentTxn(); - try { - txn.start(); - - _syncQueueDao.ensureQueue(syncObjType, syncObjId); - SyncQueueVO queueVO = _syncQueueDao.find(syncObjType, syncObjId); - if(queueVO == null) - throw new CloudRuntimeException("Unable to queue item into DB, DB is full?"); + try { + txn.start(); - queueVO.setQueueSizeLimit(queueSizeLimit); - _syncQueueDao.update(queueVO.getId(), queueVO); - - Date dt = DateUtil.currentGMTTime(); - SyncQueueItemVO item = new SyncQueueItemVO(); - item.setQueueId(queueVO.getId()); - item.setContentType(itemType); - item.setContentId(itemId); - item.setCreated(dt); - - _syncQueueItemDao.persist(item); - txn.commit(); - - return queueVO; - } catch(Exception e) { - s_logger.error("Unexpected exception: ", e); - txn.rollback(); - } - return null; + _syncQueueDao.ensureQueue(syncObjType, syncObjId); + SyncQueueVO queueVO = _syncQueueDao.find(syncObjType, syncObjId); + if(queueVO == null) + throw new CloudRuntimeException("Unable to queue item into DB, DB is full?"); + + queueVO.setQueueSizeLimit(queueSizeLimit); + _syncQueueDao.update(queueVO.getId(), queueVO); + + Date dt = DateUtil.currentGMTTime(); + SyncQueueItemVO item = new SyncQueueItemVO(); + item.setQueueId(queueVO.getId()); + item.setContentType(itemType); + item.setContentId(itemId); + item.setCreated(dt); + + _syncQueueItemDao.persist(item); + txn.commit(); + + return queueVO; + } catch(Exception e) { + s_logger.error("Unexpected exception: ", e); + txn.rollback(); + } + return null; } - + @Override @DB public SyncQueueItemVO dequeueFromOne(long queueId, Long msid) { - Transaction txt = Transaction.currentTxn(); - try { - txt.start(); - - SyncQueueVO queueVO = _syncQueueDao.lockRow(queueId, true); - if(queueVO == null) { - s_logger.error("Sync queue(id: " + queueId + ") does not exist"); - txt.commit(); - return null; - } - - if(queueReadyToProcess(queueVO)) { - SyncQueueItemVO itemVO = _syncQueueItemDao.getNextQueueItem(queueVO.getId()); - if(itemVO != null) { - Long processNumber = queueVO.getLastProcessNumber(); - if(processNumber == null) - processNumber = new Long(1); - else - processNumber = processNumber + 1; - Date dt = DateUtil.currentGMTTime(); - queueVO.setLastProcessNumber(processNumber); - queueVO.setLastUpdated(dt); - queueVO.setQueueSize(queueVO.getQueueSize() + 1); - _syncQueueDao.update(queueVO.getId(), queueVO); - - itemVO.setLastProcessMsid(msid); - itemVO.setLastProcessNumber(processNumber); - itemVO.setLastProcessTime(dt); - _syncQueueItemDao.update(itemVO.getId(), itemVO); - - txt.commit(); - return itemVO; - } else { - if(s_logger.isDebugEnabled()) - s_logger.debug("Sync queue (" + queueId + ") is currently empty"); - } - } else { - if(s_logger.isDebugEnabled()) - s_logger.debug("There is a pending process in sync queue(id: " + queueId + ")"); - } - txt.commit(); - } catch(Exception e) { - s_logger.error("Unexpected exception: ", e); - txt.rollback(); - } - - return null; + Transaction txt = Transaction.currentTxn(); + try { + txt.start(); + + SyncQueueVO queueVO = _syncQueueDao.lockRow(queueId, true); + if(queueVO == null) { + s_logger.error("Sync queue(id: " + queueId + ") does not exist"); + txt.commit(); + return null; + } + + if(queueReadyToProcess(queueVO)) { + SyncQueueItemVO itemVO = _syncQueueItemDao.getNextQueueItem(queueVO.getId()); + if(itemVO != null) { + Long processNumber = queueVO.getLastProcessNumber(); + if(processNumber == null) + processNumber = new Long(1); + else + processNumber = processNumber + 1; + Date dt = DateUtil.currentGMTTime(); + queueVO.setLastProcessNumber(processNumber); + queueVO.setLastUpdated(dt); + queueVO.setQueueSize(queueVO.getQueueSize() + 1); + _syncQueueDao.update(queueVO.getId(), queueVO); + + itemVO.setLastProcessMsid(msid); + itemVO.setLastProcessNumber(processNumber); + itemVO.setLastProcessTime(dt); + _syncQueueItemDao.update(itemVO.getId(), itemVO); + + txt.commit(); + return itemVO; + } else { + if(s_logger.isDebugEnabled()) + s_logger.debug("Sync queue (" + queueId + ") is currently empty"); + } + } else { + if(s_logger.isDebugEnabled()) + s_logger.debug("There is a pending process in sync queue(id: " + queueId + ")"); + } + txt.commit(); + } catch(Exception e) { + s_logger.error("Unexpected exception: ", e); + txt.rollback(); + } + + return null; } - + @Override @DB public List dequeueFromAny(Long msid, int maxItems) { - - List resultList = new ArrayList(); - Transaction txt = Transaction.currentTxn(); - try { - txt.start(); - - List l = _syncQueueItemDao.getNextQueueItems(maxItems); - if(l != null && l.size() > 0) { - for(SyncQueueItemVO item : l) { - SyncQueueVO queueVO = _syncQueueDao.lockRow(item.getQueueId(), true); - SyncQueueItemVO itemVO = _syncQueueItemDao.lockRow(item.getId(), true); - if(queueReadyToProcess(queueVO) && itemVO.getLastProcessNumber() == null) { - Long processNumber = queueVO.getLastProcessNumber(); - if(processNumber == null) - processNumber = new Long(1); - else - processNumber = processNumber + 1; - - Date dt = DateUtil.currentGMTTime(); - queueVO.setLastProcessNumber(processNumber); - queueVO.setLastUpdated(dt); - queueVO.setQueueSize(queueVO.getQueueSize() + 1); - _syncQueueDao.update(queueVO.getId(), queueVO); - - itemVO.setLastProcessMsid(msid); - itemVO.setLastProcessNumber(processNumber); - itemVO.setLastProcessTime(dt); - _syncQueueItemDao.update(item.getId(), itemVO); - - resultList.add(item); - } - } - } - txt.commit(); - return resultList; - } catch(Exception e) { - s_logger.error("Unexpected exception: ", e); - txt.rollback(); - } - return null; + + List resultList = new ArrayList(); + Transaction txt = Transaction.currentTxn(); + try { + txt.start(); + + List l = _syncQueueItemDao.getNextQueueItems(maxItems); + if(l != null && l.size() > 0) { + for(SyncQueueItemVO item : l) { + SyncQueueVO queueVO = _syncQueueDao.lockRow(item.getQueueId(), true); + SyncQueueItemVO itemVO = _syncQueueItemDao.lockRow(item.getId(), true); + if(queueReadyToProcess(queueVO) && itemVO.getLastProcessNumber() == null) { + Long processNumber = queueVO.getLastProcessNumber(); + if(processNumber == null) + processNumber = new Long(1); + else + processNumber = processNumber + 1; + + Date dt = DateUtil.currentGMTTime(); + queueVO.setLastProcessNumber(processNumber); + queueVO.setLastUpdated(dt); + queueVO.setQueueSize(queueVO.getQueueSize() + 1); + _syncQueueDao.update(queueVO.getId(), queueVO); + + itemVO.setLastProcessMsid(msid); + itemVO.setLastProcessNumber(processNumber); + itemVO.setLastProcessTime(dt); + _syncQueueItemDao.update(item.getId(), itemVO); + + resultList.add(item); + } + } + } + txt.commit(); + return resultList; + } catch(Exception e) { + s_logger.error("Unexpected exception: ", e); + txt.rollback(); + } + return null; } - + @Override @DB public void purgeItem(long queueItemId) { - Transaction txt = Transaction.currentTxn(); - try { - txt.start(); - - SyncQueueItemVO itemVO = _syncQueueItemDao.findById(queueItemId); - if(itemVO != null) { - SyncQueueVO queueVO = _syncQueueDao.lockRow(itemVO.getQueueId(), true); - - _syncQueueItemDao.expunge(itemVO.getId()); - - //if item is active, reset queue information - if (itemVO.getLastProcessMsid() != null) { - queueVO.setLastUpdated(DateUtil.currentGMTTime()); - //decrement the count - assert (queueVO.getQueueSize() > 0) : "Count reduce happens when it's already <= 0!"; - queueVO.setQueueSize(queueVO.getQueueSize() - 1); - _syncQueueDao.update(queueVO.getId(), queueVO); - } - } - txt.commit(); - } catch(Exception e) { - s_logger.error("Unexpected exception: ", e); - txt.rollback(); - } + Transaction txt = Transaction.currentTxn(); + try { + txt.start(); + + SyncQueueItemVO itemVO = _syncQueueItemDao.findById(queueItemId); + if(itemVO != null) { + SyncQueueVO queueVO = _syncQueueDao.lockRow(itemVO.getQueueId(), true); + + _syncQueueItemDao.expunge(itemVO.getId()); + + //if item is active, reset queue information + if (itemVO.getLastProcessMsid() != null) { + queueVO.setLastUpdated(DateUtil.currentGMTTime()); + //decrement the count + assert (queueVO.getQueueSize() > 0) : "Count reduce happens when it's already <= 0!"; + queueVO.setQueueSize(queueVO.getQueueSize() - 1); + _syncQueueDao.update(queueVO.getId(), queueVO); + } + } + txt.commit(); + } catch(Exception e) { + s_logger.error("Unexpected exception: ", e); + txt.rollback(); + } } - + @Override @DB public void returnItem(long queueItemId) { - Transaction txt = Transaction.currentTxn(); - try { - txt.start(); - - SyncQueueItemVO itemVO = _syncQueueItemDao.findById(queueItemId); - if(itemVO != null) { - SyncQueueVO queueVO = _syncQueueDao.lockRow(itemVO.getQueueId(), true); - - itemVO.setLastProcessMsid(null); - itemVO.setLastProcessNumber(null); - itemVO.setLastProcessTime(null); - _syncQueueItemDao.update(queueItemId, itemVO); - - queueVO.setLastUpdated(DateUtil.currentGMTTime()); - _syncQueueDao.update(queueVO.getId(), queueVO); - } - txt.commit(); - } catch(Exception e) { - s_logger.error("Unexpected exception: ", e); - txt.rollback(); - } + Transaction txt = Transaction.currentTxn(); + try { + txt.start(); + + SyncQueueItemVO itemVO = _syncQueueItemDao.findById(queueItemId); + if(itemVO != null) { + SyncQueueVO queueVO = _syncQueueDao.lockRow(itemVO.getQueueId(), true); + + itemVO.setLastProcessMsid(null); + itemVO.setLastProcessNumber(null); + itemVO.setLastProcessTime(null); + _syncQueueItemDao.update(queueItemId, itemVO); + + queueVO.setLastUpdated(DateUtil.currentGMTTime()); + _syncQueueDao.update(queueVO.getId(), queueVO); + } + txt.commit(); + } catch(Exception e) { + s_logger.error("Unexpected exception: ", e); + txt.rollback(); + } } - + @Override - public List getActiveQueueItems(Long msid, boolean exclusive) { - return _syncQueueItemDao.getActiveQueueItems(msid, exclusive); + public List getActiveQueueItems(Long msid, boolean exclusive) { + return _syncQueueItemDao.getActiveQueueItems(msid, exclusive); } - + @Override public List getBlockedQueueItems(long thresholdMs, boolean exclusive) { return _syncQueueItemDao.getBlockedQueueItems(thresholdMs, exclusive); } - + @Override public boolean configure(String name, Map params) throws ConfigurationException { - _name = name; - return true; + _name = name; + return true; } - + @Override public boolean start() { return true; @@ -256,16 +255,16 @@ public class SyncQueueManagerImpl implements SyncQueueManager { public boolean stop() { return true; } - + @Override public String getName() { - return _name; + return _name; } private boolean queueReadyToProcess(SyncQueueVO queueVO) { return queueVO.getQueueSize() < queueVO.getQueueSizeLimit(); } - + @Override public void purgeAsyncJobQueueItemId(long asyncJobId) { Long itemId = _syncQueueItemDao.getQueueItemIdByContentIdAndType(asyncJobId, SyncQueueItem.AsyncJobContentType); diff --git a/server/src/com/cloud/capacity/dao/CapacityDaoImpl.java b/server/src/com/cloud/capacity/dao/CapacityDaoImpl.java index e55bef69e6c..baaf39164cd 100755 --- a/server/src/com/cloud/capacity/dao/CapacityDaoImpl.java +++ b/server/src/com/cloud/capacity/dao/CapacityDaoImpl.java @@ -35,10 +35,7 @@ import com.cloud.capacity.CapacityVO; import com.cloud.storage.Storage; import com.cloud.storage.StoragePoolVO; import com.cloud.storage.dao.StoragePoolDao; -import com.cloud.storage.dao.StoragePoolDaoImpl; import com.cloud.utils.Pair; -import com.cloud.utils.StringUtils; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; @@ -60,116 +57,116 @@ public class CapacityDaoImpl extends GenericDaoBase implements private static final String LIST_CLUSTERSINZONE_BY_HOST_CAPACITIES_PART1 = "SELECT DISTINCT capacity.cluster_id FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`cluster` cluster on (cluster.id = capacity.cluster_id AND cluster.removed is NULL) WHERE "; private static final String LIST_CLUSTERSINZONE_BY_HOST_CAPACITIES_PART2 = " AND capacity_type = ? AND ((total_capacity * ?) - used_capacity + reserved_capacity) >= ? " + - "AND cluster_id IN (SELECT distinct cluster_id FROM `cloud`.`op_host_capacity` WHERE "; + "AND cluster_id IN (SELECT distinct cluster_id FROM `cloud`.`op_host_capacity` WHERE "; private static final String LIST_CLUSTERSINZONE_BY_HOST_CAPACITIES_PART3 = " AND capacity_type = ? AND ((total_capacity * ?) - used_capacity + reserved_capacity) >= ?) "; - + private final SearchBuilder _hostIdTypeSearch; - private final SearchBuilder _hostOrPoolIdSearch; + private final SearchBuilder _hostOrPoolIdSearch; protected GenericSearchBuilder SummedCapacitySearch; - private SearchBuilder _allFieldsSearch; + private final SearchBuilder _allFieldsSearch; @Inject protected StoragePoolDao _storagePoolDao; - + private static final String LIST_HOSTS_IN_CLUSTER_WITH_ENOUGH_CAPACITY = "SELECT a.host_id FROM (host JOIN op_host_capacity a ON host.id = a.host_id AND host.cluster_id = ? AND host.type = ? " + - "AND (a.total_capacity * ? - a.used_capacity) >= ? and a.capacity_type = 1) " + - "JOIN op_host_capacity b ON a.host_id = b.host_id AND b.total_capacity - b.used_capacity >= ? AND b.capacity_type = 0"; - + "AND (a.total_capacity * ? - a.used_capacity) >= ? and a.capacity_type = 1) " + + "JOIN op_host_capacity b ON a.host_id = b.host_id AND b.total_capacity - b.used_capacity >= ? AND b.capacity_type = 0"; + private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART1 = "SELECT cluster_id, SUM(used_capacity+reserved_capacity)/SUM(total_capacity * ?) FROM `cloud`.`op_host_capacity` WHERE " ; private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART2 = " AND capacity_type = ? GROUP BY cluster_id ORDER BY SUM(used_capacity+reserved_capacity)/SUM(total_capacity * ?) ASC"; - + private static final String LIST_PODSINZONE_BY_HOST_CAPACITIES = "SELECT DISTINCT capacity.pod_id FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`host_pod_ref` pod " + - " ON (pod.id = capacity.pod_id AND pod.removed is NULL) WHERE " + - " capacity.data_center_id = ? AND capacity_type = ? AND ((total_capacity * ?) - used_capacity + reserved_capacity) >= ? " + - " AND pod_id IN (SELECT distinct pod_id FROM `cloud`.`op_host_capacity` WHERE " + - " capacity.data_center_id = ? AND capacity_type = ? AND ((total_capacity * ?) - used_capacity + reserved_capacity) >= ?) "; + " ON (pod.id = capacity.pod_id AND pod.removed is NULL) WHERE " + + " capacity.data_center_id = ? AND capacity_type = ? AND ((total_capacity * ?) - used_capacity + reserved_capacity) >= ? " + + " AND pod_id IN (SELECT distinct pod_id FROM `cloud`.`op_host_capacity` WHERE " + + " capacity.data_center_id = ? AND capacity_type = ? AND ((total_capacity * ?) - used_capacity + reserved_capacity) >= ?) "; private static final String ORDER_PODS_BY_AGGREGATE_CAPACITY = "SELECT pod_id, SUM(used_capacity+reserved_capacity)/SUM(total_capacity * ?) FROM `cloud`.`op_host_capacity` WHERE data_center_id = ? " + - " AND capacity_type = ? GROUP BY pod_id ORDER BY SUM(used_capacity+reserved_capacity)/SUM(total_capacity * ?) ASC"; - + " AND capacity_type = ? GROUP BY pod_id ORDER BY SUM(used_capacity+reserved_capacity)/SUM(total_capacity * ?) ASC"; + private static final String LIST_CAPACITY_BY_RESOURCE_STATE = "SELECT capacity.data_center_id, sum(capacity.used_capacity), sum(capacity.reserved_quantity), sum(capacity.total_capacity), capacity_capacity_type "+ - "FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`data_center` dc ON (dc.id = capacity.data_center_id AND dc.removed is NULL)"+ - "FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`host_pod_ref` pod ON (pod.id = capacity.pod_id AND pod.removed is NULL)"+ - "FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`cluster` cluster ON (cluster.id = capacity.cluster_id AND cluster.removed is NULL)"+ - "FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`host` host ON (host.id = capacity.host_id AND host.removed is NULL)"+ - "WHERE dc.allocation_state = ? AND pod.allocation_state = ? AND cluster.allocation_state = ? AND host.resource_state = ? AND capacity_type not in (3,4) "; - + "FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`data_center` dc ON (dc.id = capacity.data_center_id AND dc.removed is NULL)"+ + "FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`host_pod_ref` pod ON (pod.id = capacity.pod_id AND pod.removed is NULL)"+ + "FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`cluster` cluster ON (cluster.id = capacity.cluster_id AND cluster.removed is NULL)"+ + "FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`host` host ON (host.id = capacity.host_id AND host.removed is NULL)"+ + "WHERE dc.allocation_state = ? AND pod.allocation_state = ? AND cluster.allocation_state = ? AND host.resource_state = ? AND capacity_type not in (3,4) "; + private static final String LIST_CAPACITY_GROUP_BY_ZONE_TYPE_PART1 = "SELECT (sum(capacity.used_capacity) + sum(capacity.reserved_capacity)), (case capacity_type when 1 then (sum(total_capacity) * (select value from `cloud`.`configuration` where name like 'cpu.overprovisioning.factor')) else sum(total_capacity) end), " + - "((sum(capacity.used_capacity) + sum(capacity.reserved_capacity)) / (case capacity_type when 1 then (sum(total_capacity) * (select value from `cloud`.`configuration` where name like 'cpu.overprovisioning.factor')) else sum(total_capacity) end)) percent,"+ - " capacity.capacity_type, capacity.data_center_id "+ - "FROM `cloud`.`op_host_capacity` capacity "+ - "WHERE total_capacity > 0 AND data_center_id is not null AND capacity_state='Enabled'"; + "((sum(capacity.used_capacity) + sum(capacity.reserved_capacity)) / (case capacity_type when 1 then (sum(total_capacity) * (select value from `cloud`.`configuration` where name like 'cpu.overprovisioning.factor')) else sum(total_capacity) end)) percent,"+ + " capacity.capacity_type, capacity.data_center_id "+ + "FROM `cloud`.`op_host_capacity` capacity "+ + "WHERE total_capacity > 0 AND data_center_id is not null AND capacity_state='Enabled'"; private static final String LIST_CAPACITY_GROUP_BY_ZONE_TYPE_PART2 = " GROUP BY data_center_id, capacity_type order by percent desc limit "; private static final String LIST_CAPACITY_GROUP_BY_POD_TYPE_PART1 = "SELECT (sum(capacity.used_capacity) + sum(capacity.reserved_capacity)), (case capacity_type when 1 then (sum(total_capacity) * (select value from `cloud`.`configuration` where name like 'cpu.overprovisioning.factor')) else sum(total_capacity) end), " + - "((sum(capacity.used_capacity) + sum(capacity.reserved_capacity)) / (case capacity_type when 1 then (sum(total_capacity) * (select value from `cloud`.`configuration` where name like 'cpu.overprovisioning.factor')) else sum(total_capacity) end)) percent,"+ - " capacity.capacity_type, capacity.data_center_id, pod_id "+ - "FROM `cloud`.`op_host_capacity` capacity "+ - "WHERE total_capacity > 0 AND pod_id is not null AND capacity_state='Enabled'"; + "((sum(capacity.used_capacity) + sum(capacity.reserved_capacity)) / (case capacity_type when 1 then (sum(total_capacity) * (select value from `cloud`.`configuration` where name like 'cpu.overprovisioning.factor')) else sum(total_capacity) end)) percent,"+ + " capacity.capacity_type, capacity.data_center_id, pod_id "+ + "FROM `cloud`.`op_host_capacity` capacity "+ + "WHERE total_capacity > 0 AND pod_id is not null AND capacity_state='Enabled'"; private static final String LIST_CAPACITY_GROUP_BY_POD_TYPE_PART2 = " GROUP BY pod_id, capacity_type order by percent desc limit "; - + private static final String LIST_CAPACITY_GROUP_BY_CLUSTER_TYPE_PART1 = "SELECT (sum(capacity.used_capacity) + sum(capacity.reserved_capacity)), (case capacity_type when 1 then (sum(total_capacity) * (select value from `cloud`.`configuration` where name like 'cpu.overprovisioning.factor')) else sum(total_capacity) end), " + - "((sum(capacity.used_capacity) + sum(capacity.reserved_capacity)) / (case capacity_type when 1 then (sum(total_capacity) * (select value from `cloud`.`configuration` where name like 'cpu.overprovisioning.factor')) else sum(total_capacity) end)) percent,"+ - "capacity.capacity_type, capacity.data_center_id, pod_id, cluster_id "+ - "FROM `cloud`.`op_host_capacity` capacity "+ - "WHERE total_capacity > 0 AND cluster_id is not null AND capacity_state='Enabled'"; + "((sum(capacity.used_capacity) + sum(capacity.reserved_capacity)) / (case capacity_type when 1 then (sum(total_capacity) * (select value from `cloud`.`configuration` where name like 'cpu.overprovisioning.factor')) else sum(total_capacity) end)) percent,"+ + "capacity.capacity_type, capacity.data_center_id, pod_id, cluster_id "+ + "FROM `cloud`.`op_host_capacity` capacity "+ + "WHERE total_capacity > 0 AND cluster_id is not null AND capacity_state='Enabled'"; private static final String LIST_CAPACITY_GROUP_BY_CLUSTER_TYPE_PART2 = " GROUP BY cluster_id, capacity_type order by percent desc limit "; private static final String UPDATE_CAPACITY_STATE = "UPDATE `cloud`.`op_host_capacity` SET capacity_state = ? WHERE "; private static final String LIST_CLUSTERS_CROSSING_THRESHOLD = "SELECT cluster_id " + - "FROM (SELECT cluster_id, ( (sum(capacity.used_capacity) + sum(capacity.reserved_capacity) + ?)/sum(total_capacity) ) ratio "+ - "FROM `cloud`.`op_host_capacity` capacity "+ - "WHERE capacity.data_center_id = ? AND capacity.capacity_type = ? AND capacity.total_capacity > 0 "+ - "GROUP BY cluster_id) tmp " + - "WHERE tmp.ratio > ? "; - - + "FROM (SELECT cluster_id, ( (sum(capacity.used_capacity) + sum(capacity.reserved_capacity) + ?)/sum(total_capacity) ) ratio "+ + "FROM `cloud`.`op_host_capacity` capacity "+ + "WHERE capacity.data_center_id = ? AND capacity.capacity_type = ? AND capacity.total_capacity > 0 "+ + "GROUP BY cluster_id) tmp " + + "WHERE tmp.ratio > ? "; + + public CapacityDaoImpl() { - _hostIdTypeSearch = createSearchBuilder(); - _hostIdTypeSearch.and("hostId", _hostIdTypeSearch.entity().getHostOrPoolId(), SearchCriteria.Op.EQ); - _hostIdTypeSearch.and("type", _hostIdTypeSearch.entity().getCapacityType(), SearchCriteria.Op.EQ); - _hostIdTypeSearch.done(); - - _hostOrPoolIdSearch = createSearchBuilder(); - _hostOrPoolIdSearch.and("hostId", _hostOrPoolIdSearch.entity().getHostOrPoolId(), SearchCriteria.Op.EQ); - _hostOrPoolIdSearch.done(); - - _allFieldsSearch = createSearchBuilder(); - _allFieldsSearch.and("id", _allFieldsSearch.entity().getId(), SearchCriteria.Op.EQ); - _allFieldsSearch.and("hostId", _allFieldsSearch.entity().getHostOrPoolId(), SearchCriteria.Op.EQ); - _allFieldsSearch.and("zoneId", _allFieldsSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); - _allFieldsSearch.and("podId", _allFieldsSearch.entity().getPodId(), SearchCriteria.Op.EQ); - _allFieldsSearch.and("clusterId", _allFieldsSearch.entity().getClusterId(), SearchCriteria.Op.EQ); - _allFieldsSearch.and("capacityType", _allFieldsSearch.entity().getCapacityType(), SearchCriteria.Op.EQ); - _allFieldsSearch.and("capacityState", _allFieldsSearch.entity().getCapacityState(), SearchCriteria.Op.EQ); - - _allFieldsSearch.done(); + _hostIdTypeSearch = createSearchBuilder(); + _hostIdTypeSearch.and("hostId", _hostIdTypeSearch.entity().getHostOrPoolId(), SearchCriteria.Op.EQ); + _hostIdTypeSearch.and("type", _hostIdTypeSearch.entity().getCapacityType(), SearchCriteria.Op.EQ); + _hostIdTypeSearch.done(); + + _hostOrPoolIdSearch = createSearchBuilder(); + _hostOrPoolIdSearch.and("hostId", _hostOrPoolIdSearch.entity().getHostOrPoolId(), SearchCriteria.Op.EQ); + _hostOrPoolIdSearch.done(); + + _allFieldsSearch = createSearchBuilder(); + _allFieldsSearch.and("id", _allFieldsSearch.entity().getId(), SearchCriteria.Op.EQ); + _allFieldsSearch.and("hostId", _allFieldsSearch.entity().getHostOrPoolId(), SearchCriteria.Op.EQ); + _allFieldsSearch.and("zoneId", _allFieldsSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + _allFieldsSearch.and("podId", _allFieldsSearch.entity().getPodId(), SearchCriteria.Op.EQ); + _allFieldsSearch.and("clusterId", _allFieldsSearch.entity().getClusterId(), SearchCriteria.Op.EQ); + _allFieldsSearch.and("capacityType", _allFieldsSearch.entity().getCapacityType(), SearchCriteria.Op.EQ); + _allFieldsSearch.and("capacityState", _allFieldsSearch.entity().getCapacityState(), SearchCriteria.Op.EQ); + + _allFieldsSearch.done(); } - + @Override public List listClustersCrossingThreshold(short capacityType, Long zoneId, Float disableThreshold, long compute_requested, Float overProvFactor){ - - Transaction txn = Transaction.currentTxn(); - PreparedStatement pstmt = null; - List result = new ArrayList(); - StringBuilder sql = new StringBuilder(LIST_CLUSTERS_CROSSING_THRESHOLD); - - - try { - pstmt = txn.prepareAutoCloseStatement(sql.toString()); - pstmt.setLong(1, compute_requested); - pstmt.setLong(2, zoneId); - pstmt.setShort(3, capacityType); - pstmt.setFloat(4, disableThreshold*overProvFactor); - - ResultSet rs = pstmt.executeQuery(); - while (rs.next()) { - result.add(rs.getLong(1)); - } - return result; - } catch (SQLException e) { - throw new CloudRuntimeException("DB Exception on: " + sql, e); - } catch (Throwable e) { - throw new CloudRuntimeException("Caught: " + sql, e); - } - } + + Transaction txn = Transaction.currentTxn(); + PreparedStatement pstmt = null; + List result = new ArrayList(); + StringBuilder sql = new StringBuilder(LIST_CLUSTERS_CROSSING_THRESHOLD); + + + try { + pstmt = txn.prepareAutoCloseStatement(sql.toString()); + pstmt.setLong(1, compute_requested); + pstmt.setLong(2, zoneId); + pstmt.setShort(3, capacityType); + pstmt.setFloat(4, disableThreshold*overProvFactor); + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + result.add(rs.getLong(1)); + } + return result; + } catch (SQLException e) { + throw new CloudRuntimeException("DB Exception on: " + sql, e); + } catch (Throwable e) { + throw new CloudRuntimeException("Caught: " + sql, e); + } + } /*public static String preparePlaceHolders(int length) { StringBuilder builder = new StringBuilder(); @@ -188,17 +185,17 @@ public class CapacityDaoImpl extends GenericDaoBase implements } }*/ - + @Override public List findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId, String resource_state){ - + Transaction txn = Transaction.currentTxn(); PreparedStatement pstmt = null; List result = new ArrayList(); StringBuilder sql = new StringBuilder(LIST_CAPACITY_BY_RESOURCE_STATE); List resourceIdList = new ArrayList(); - + if (zoneId != null){ sql.append(" AND capacity.data_center_id = ?"); resourceIdList.add(zoneId); @@ -237,29 +234,29 @@ public class CapacityDaoImpl extends GenericDaoBase implements throw new CloudRuntimeException("Caught: " + sql, e); } } - + @Override public List listCapacitiesGroupedByLevelAndType(Integer capacityType, Long zoneId, Long podId, Long clusterId, int level, Long limit){ - + StringBuilder finalQuery = new StringBuilder(); Transaction txn = Transaction.currentTxn(); PreparedStatement pstmt = null; List result = new ArrayList(); - + switch(level){ - case 1: // List all the capacities grouped by zone, capacity Type - finalQuery.append(LIST_CAPACITY_GROUP_BY_ZONE_TYPE_PART1); - break; - - case 2: // List all the capacities grouped by pod, capacity Type - finalQuery.append(LIST_CAPACITY_GROUP_BY_POD_TYPE_PART1); - break; - - case 3: // List all the capacities grouped by cluster, capacity Type - finalQuery.append(LIST_CAPACITY_GROUP_BY_CLUSTER_TYPE_PART1); - break; + case 1: // List all the capacities grouped by zone, capacity Type + finalQuery.append(LIST_CAPACITY_GROUP_BY_ZONE_TYPE_PART1); + break; + + case 2: // List all the capacities grouped by pod, capacity Type + finalQuery.append(LIST_CAPACITY_GROUP_BY_POD_TYPE_PART1); + break; + + case 3: // List all the capacities grouped by cluster, capacity Type + finalQuery.append(LIST_CAPACITY_GROUP_BY_CLUSTER_TYPE_PART1); + break; } - + if (zoneId != null){ finalQuery.append(" AND data_center_id="+zoneId); } @@ -272,32 +269,32 @@ public class CapacityDaoImpl extends GenericDaoBase implements if (capacityType != null){ finalQuery.append(" AND capacity_type="+capacityType); } - + switch(level){ case 1: // List all the capacities grouped by zone, capacity Type finalQuery.append(LIST_CAPACITY_GROUP_BY_ZONE_TYPE_PART2); break; - + case 2: // List all the capacities grouped by pod, capacity Type finalQuery.append(LIST_CAPACITY_GROUP_BY_POD_TYPE_PART2); break; - + case 3: // List all the capacities grouped by cluster, capacity Type finalQuery.append(LIST_CAPACITY_GROUP_BY_CLUSTER_TYPE_PART2); break; } - + finalQuery.append(limit.toString()); - + try { pstmt = txn.prepareAutoCloseStatement(finalQuery.toString()); ResultSet rs = pstmt.executeQuery(); while (rs.next()) { SummedCapacity summedCapacity = new SummedCapacity( rs.getLong(1), rs.getLong(2), rs.getFloat(3), - (short)rs.getLong(4), rs.getLong(5), - level != 1 ? rs.getLong(6): null, - level == 3 ? rs.getLong(7): null); - + (short)rs.getLong(4), rs.getLong(5), + level != 1 ? rs.getLong(6): null, + level == 3 ? rs.getLong(7): null); + result.add(summedCapacity); } return result; @@ -306,61 +303,61 @@ public class CapacityDaoImpl extends GenericDaoBase implements } catch (Throwable e) { throw new CloudRuntimeException("Caught: " + finalQuery, e); } - + } - + @Override public List findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId){ - - SummedCapacitySearch = createSearchBuilder(SummedCapacity.class); - SummedCapacitySearch.select("dcId", Func.NATIVE, SummedCapacitySearch.entity().getDataCenterId()); + + SummedCapacitySearch = createSearchBuilder(SummedCapacity.class); + SummedCapacitySearch.select("dcId", Func.NATIVE, SummedCapacitySearch.entity().getDataCenterId()); SummedCapacitySearch.select("sumUsed", Func.SUM, SummedCapacitySearch.entity().getUsedCapacity()); SummedCapacitySearch.select("sumReserved", Func.SUM, SummedCapacitySearch.entity().getReservedCapacity()); SummedCapacitySearch.select("sumTotal", Func.SUM, SummedCapacitySearch.entity().getTotalCapacity()); SummedCapacitySearch.select("capacityType", Func.NATIVE, SummedCapacitySearch.entity().getCapacityType()); - + if (zoneId==null && podId==null && clusterId==null){ // List all the capacities grouped by zone, capacity Type SummedCapacitySearch.groupBy(SummedCapacitySearch.entity().getDataCenterId(), SummedCapacitySearch.entity().getCapacityType()); }else { SummedCapacitySearch.groupBy(SummedCapacitySearch.entity().getCapacityType()); } - + if (zoneId != null){ - SummedCapacitySearch.and("dcId", SummedCapacitySearch.entity().getDataCenterId(), Op.EQ); + SummedCapacitySearch.and("dcId", SummedCapacitySearch.entity().getDataCenterId(), Op.EQ); } if (podId != null){ - SummedCapacitySearch.and("podId", SummedCapacitySearch.entity().getPodId(), Op.EQ); + SummedCapacitySearch.and("podId", SummedCapacitySearch.entity().getPodId(), Op.EQ); } if (clusterId != null){ - SummedCapacitySearch.and("clusterId", SummedCapacitySearch.entity().getClusterId(), Op.EQ); + SummedCapacitySearch.and("clusterId", SummedCapacitySearch.entity().getClusterId(), Op.EQ); } if (capacityType != null){ - SummedCapacitySearch.and("capacityType", SummedCapacitySearch.entity().getCapacityType(), Op.EQ); + SummedCapacitySearch.and("capacityType", SummedCapacitySearch.entity().getCapacityType(), Op.EQ); } SummedCapacitySearch.done(); - - + + SearchCriteria sc = SummedCapacitySearch.create(); if (zoneId != null){ - sc.setParameters("dcId", zoneId); + sc.setParameters("dcId", zoneId); } if (podId != null){ - sc.setParameters("podId", podId); + sc.setParameters("podId", podId); } if (clusterId != null){ - sc.setParameters("clusterId", clusterId); + sc.setParameters("clusterId", clusterId); } if (capacityType != null){ - sc.setParameters("capacityType", capacityType); + sc.setParameters("capacityType", capacityType); } - + Filter filter = new Filter(CapacityVO.class, null, true, null, null); List results = customSearchIncludingRemoved(sc, filter); return results; - + } - + public void updateAllocated(Long hostId, long allocatedAmount, short capacityType, boolean add) { Transaction txn = Transaction.currentTxn(); PreparedStatement pstmt = null; @@ -384,75 +381,47 @@ public class CapacityDaoImpl extends GenericDaoBase implements } } - + @Override public CapacityVO findByHostIdType(Long hostId, short capacityType) { - SearchCriteria sc = _hostIdTypeSearch.create(); - sc.setParameters("hostId", hostId); - sc.setParameters("type", capacityType); - return findOneBy(sc); + SearchCriteria sc = _hostIdTypeSearch.create(); + sc.setParameters("hostId", hostId); + sc.setParameters("type", capacityType); + return findOneBy(sc); } - + @Override public List listClustersInZoneOrPodByHostCapacities(long id, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone, float cpuOverprovisioningFactor){ - Transaction txn = Transaction.currentTxn(); + Transaction txn = Transaction.currentTxn(); PreparedStatement pstmt = null; List result = new ArrayList(); StringBuilder sql = new StringBuilder(LIST_CLUSTERSINZONE_BY_HOST_CAPACITIES_PART1); - + if(isZone){ - sql.append("capacity.data_center_id = ?"); + sql.append("capacity.data_center_id = ?"); }else{ - sql.append("capacity.pod_id = ?"); + sql.append("capacity.pod_id = ?"); } sql.append(LIST_CLUSTERSINZONE_BY_HOST_CAPACITIES_PART2); if(isZone){ - sql.append("capacity.data_center_id = ?"); + sql.append("capacity.data_center_id = ?"); }else{ - sql.append("capacity.pod_id = ?"); + sql.append("capacity.pod_id = ?"); } sql.append(LIST_CLUSTERSINZONE_BY_HOST_CAPACITIES_PART3); try { pstmt = txn.prepareAutoCloseStatement(sql.toString()); pstmt.setLong(1, id); - pstmt.setShort(2, CapacityVO.CAPACITY_TYPE_CPU); - pstmt.setFloat(3, cpuOverprovisioningFactor); - pstmt.setLong(4, requiredCpu); - pstmt.setLong(5, id); - pstmt.setShort(6, CapacityVO.CAPACITY_TYPE_MEMORY); - pstmt.setFloat(7, 1); - pstmt.setLong(8, requiredRam); + pstmt.setShort(2, CapacityVO.CAPACITY_TYPE_CPU); + pstmt.setFloat(3, cpuOverprovisioningFactor); + pstmt.setLong(4, requiredCpu); + pstmt.setLong(5, id); + pstmt.setShort(6, CapacityVO.CAPACITY_TYPE_MEMORY); + pstmt.setFloat(7, 1); + pstmt.setLong(8, requiredRam); - ResultSet rs = pstmt.executeQuery(); - while (rs.next()) { - result.add(rs.getLong(1)); - } - return result; - } catch (SQLException e) { - throw new CloudRuntimeException("DB Exception on: " + sql, e); - } catch (Throwable e) { - throw new CloudRuntimeException("Caught: " + sql, e); - } - } - - - @Override - public List listHostsWithEnoughCapacity(int requiredCpu, long requiredRam, Long clusterId, String hostType, float cpuOverprovisioningFactor){ - Transaction txn = Transaction.currentTxn(); - PreparedStatement pstmt = null; - List result = new ArrayList(); - - StringBuilder sql = new StringBuilder(LIST_HOSTS_IN_CLUSTER_WITH_ENOUGH_CAPACITY); - try { - pstmt = txn.prepareAutoCloseStatement(sql.toString()); - pstmt.setLong(1, clusterId); - pstmt.setString(2, hostType); - pstmt.setFloat(3, cpuOverprovisioningFactor); - pstmt.setLong(4, requiredCpu); - pstmt.setLong(5, requiredRam); - ResultSet rs = pstmt.executeQuery(); while (rs.next()) { result.add(rs.getLong(1)); @@ -464,61 +433,89 @@ public class CapacityDaoImpl extends GenericDaoBase implements throw new CloudRuntimeException("Caught: " + sql, e); } } - - public static class SummedCapacity { - public long sumUsed; - public long sumReserved; - public long sumTotal; - public Float percentUsed; - public short capacityType; - public Long clusterId; - public Long podId; - public Long dcId; - public SummedCapacity() { - } - public SummedCapacity(long sumUsed, long sumReserved, long sumTotal, - short capacityType, Long clusterId, Long podId) { - super(); - this.sumUsed = sumUsed; - this.sumReserved = sumReserved; - this.sumTotal = sumTotal; - this.capacityType = capacityType; - this.clusterId = clusterId; - this.podId = podId; - } - public SummedCapacity(long sumUsed, long sumReserved, long sumTotal, + + + @Override + public List listHostsWithEnoughCapacity(int requiredCpu, long requiredRam, Long clusterId, String hostType, float cpuOverprovisioningFactor){ + Transaction txn = Transaction.currentTxn(); + PreparedStatement pstmt = null; + List result = new ArrayList(); + + StringBuilder sql = new StringBuilder(LIST_HOSTS_IN_CLUSTER_WITH_ENOUGH_CAPACITY); + try { + pstmt = txn.prepareAutoCloseStatement(sql.toString()); + pstmt.setLong(1, clusterId); + pstmt.setString(2, hostType); + pstmt.setFloat(3, cpuOverprovisioningFactor); + pstmt.setLong(4, requiredCpu); + pstmt.setLong(5, requiredRam); + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + result.add(rs.getLong(1)); + } + return result; + } catch (SQLException e) { + throw new CloudRuntimeException("DB Exception on: " + sql, e); + } catch (Throwable e) { + throw new CloudRuntimeException("Caught: " + sql, e); + } + } + + public static class SummedCapacity { + public long sumUsed; + public long sumReserved; + public long sumTotal; + public Float percentUsed; + public short capacityType; + public Long clusterId; + public Long podId; + public Long dcId; + public SummedCapacity() { + } + public SummedCapacity(long sumUsed, long sumReserved, long sumTotal, + short capacityType, Long clusterId, Long podId) { + super(); + this.sumUsed = sumUsed; + this.sumReserved = sumReserved; + this.sumTotal = sumTotal; + this.capacityType = capacityType; + this.clusterId = clusterId; + this.podId = podId; + } + public SummedCapacity(long sumUsed, long sumReserved, long sumTotal, short capacityType, Long clusterId, Long podId, Long zoneId) { - this(sumUsed, sumReserved, sumTotal, capacityType, clusterId, podId); - this.dcId = zoneId; - } - - public SummedCapacity(long sumUsed, long sumTotal, float percentUsed, short capacityType, Long zoneId, Long podId, Long clusterId) { - super(); - this.sumUsed = sumUsed; - this.sumTotal = sumTotal; - this.percentUsed = percentUsed; - this.capacityType = capacityType; + this(sumUsed, sumReserved, sumTotal, capacityType, clusterId, podId); + this.dcId = zoneId; + } + + public SummedCapacity(long sumUsed, long sumTotal, float percentUsed, short capacityType, Long zoneId, Long podId, Long clusterId) { + super(); + this.sumUsed = sumUsed; + this.sumTotal = sumTotal; + this.percentUsed = percentUsed; + this.capacityType = capacityType; this.clusterId = clusterId; this.podId = podId; this.dcId = zoneId; } - - public Short getCapacityType() { - return capacityType; - } - public Long getUsedCapacity() { - return sumUsed; - } - public long getReservedCapacity() { - return sumReserved; - } - public Long getTotalCapacity() { - return sumTotal; - } - public Long getDataCenterId() { + + public Short getCapacityType() { + return capacityType; + } + public Long getUsedCapacity() { + return sumUsed; + } + public long getReservedCapacity() { + return sumReserved; + } + public Long getTotalCapacity() { + return sumTotal; + } + public Long getDataCenterId() { return dcId; } - public Long getClusterId() { + public Long getClusterId() { return clusterId; } public Long getPodId() { @@ -527,110 +524,111 @@ public class CapacityDaoImpl extends GenericDaoBase implements public Float getPercentUsed() { return percentUsed; } - } - public List findByClusterPodZone(Long zoneId, Long podId, Long clusterId){ + } + @Override + public List findByClusterPodZone(Long zoneId, Long podId, Long clusterId){ - SummedCapacitySearch = createSearchBuilder(SummedCapacity.class); + SummedCapacitySearch = createSearchBuilder(SummedCapacity.class); SummedCapacitySearch.select("sumUsed", Func.SUM, SummedCapacitySearch.entity().getUsedCapacity()); SummedCapacitySearch.select("sumTotal", Func.SUM, SummedCapacitySearch.entity().getTotalCapacity()); SummedCapacitySearch.select("capacityType", Func.NATIVE, SummedCapacitySearch.entity().getCapacityType()); SummedCapacitySearch.groupBy(SummedCapacitySearch.entity().getCapacityType()); - + if(zoneId != null){ - SummedCapacitySearch.and("zoneId", SummedCapacitySearch.entity().getDataCenterId(), Op.EQ); + SummedCapacitySearch.and("zoneId", SummedCapacitySearch.entity().getDataCenterId(), Op.EQ); } if (podId != null){ - SummedCapacitySearch.and("podId", SummedCapacitySearch.entity().getPodId(), Op.EQ); + SummedCapacitySearch.and("podId", SummedCapacitySearch.entity().getPodId(), Op.EQ); } if (clusterId != null){ - SummedCapacitySearch.and("clusterId", SummedCapacitySearch.entity().getClusterId(), Op.EQ); + SummedCapacitySearch.and("clusterId", SummedCapacitySearch.entity().getClusterId(), Op.EQ); } SummedCapacitySearch.done(); - - + + SearchCriteria sc = SummedCapacitySearch.create(); if (zoneId != null){ - sc.setParameters("zoneId", zoneId); + sc.setParameters("zoneId", zoneId); } if (podId != null){ - sc.setParameters("podId", podId); + sc.setParameters("podId", podId); } if (clusterId != null){ - sc.setParameters("clusterId", clusterId); + sc.setParameters("clusterId", clusterId); } - - return customSearchIncludingRemoved(sc, null); - } - - @Override - public List findNonSharedStorageForClusterPodZone(Long zoneId, Long podId, Long clusterId){ - SummedCapacitySearch = createSearchBuilder(SummedCapacity.class); + return customSearchIncludingRemoved(sc, null); + } + + @Override + public List findNonSharedStorageForClusterPodZone(Long zoneId, Long podId, Long clusterId){ + + SummedCapacitySearch = createSearchBuilder(SummedCapacity.class); SummedCapacitySearch.select("sumUsed", Func.SUM, SummedCapacitySearch.entity().getUsedCapacity()); SummedCapacitySearch.select("sumTotal", Func.SUM, SummedCapacitySearch.entity().getTotalCapacity()); SummedCapacitySearch.select("capacityType", Func.NATIVE, SummedCapacitySearch.entity().getCapacityType()); SummedCapacitySearch.and("capacityType", SummedCapacitySearch.entity().getCapacityType(), Op.EQ); - - SearchBuilder nonSharedStorage = _storagePoolDao.createSearchBuilder(); - nonSharedStorage.and("poolTypes", nonSharedStorage.entity().getPoolType(), SearchCriteria.Op.IN); - SummedCapacitySearch.join("nonSharedStorage", nonSharedStorage, nonSharedStorage.entity().getId(), SummedCapacitySearch.entity().getHostOrPoolId(), JoinType.INNER); - nonSharedStorage.done(); - + + SearchBuilder nonSharedStorage = _storagePoolDao.createSearchBuilder(); + nonSharedStorage.and("poolTypes", nonSharedStorage.entity().getPoolType(), SearchCriteria.Op.IN); + SummedCapacitySearch.join("nonSharedStorage", nonSharedStorage, nonSharedStorage.entity().getId(), SummedCapacitySearch.entity().getHostOrPoolId(), JoinType.INNER); + nonSharedStorage.done(); + if(zoneId != null){ - SummedCapacitySearch.and("zoneId", SummedCapacitySearch.entity().getDataCenterId(), Op.EQ); + SummedCapacitySearch.and("zoneId", SummedCapacitySearch.entity().getDataCenterId(), Op.EQ); } if (podId != null){ - SummedCapacitySearch.and("podId", SummedCapacitySearch.entity().getPodId(), Op.EQ); + SummedCapacitySearch.and("podId", SummedCapacitySearch.entity().getPodId(), Op.EQ); } if (clusterId != null){ - SummedCapacitySearch.and("clusterId", SummedCapacitySearch.entity().getClusterId(), Op.EQ); + SummedCapacitySearch.and("clusterId", SummedCapacitySearch.entity().getClusterId(), Op.EQ); } SummedCapacitySearch.done(); - - + + SearchCriteria sc = SummedCapacitySearch.create(); sc.setJoinParameters("nonSharedStorage", "poolTypes", Storage.getNonSharedStoragePoolTypes().toArray()); sc.setParameters("capacityType", Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED); if (zoneId != null){ - sc.setParameters("zoneId", zoneId); + sc.setParameters("zoneId", zoneId); } if (podId != null){ - sc.setParameters("podId", podId); + sc.setParameters("podId", podId); } if (clusterId != null){ - sc.setParameters("clusterId", clusterId); + sc.setParameters("clusterId", clusterId); } - + return customSearchIncludingRemoved(sc, null); - } - + } + @Override public boolean removeBy(Short capacityType, Long zoneId, Long podId, Long clusterId, Long hostId) { SearchCriteria sc = _allFieldsSearch.create(); - + if (capacityType != null) { sc.setParameters("capacityType", capacityType); } - + if (zoneId != null) { sc.setParameters("zoneId", zoneId); } - + if (podId != null) { sc.setParameters("podId", podId); } - + if (clusterId != null) { sc.setParameters("clusterId", clusterId); } - + if (hostId != null) { sc.setParameters("hostId", hostId); } - + return remove(sc) > 0; } - + @Override public Pair, Map> orderClustersByAggregateCapacity(long id, short capacityTypeForOrdering, boolean isZone, float cpuOverprovisioningFactor){ Transaction txn = Transaction.currentTxn(); @@ -639,7 +637,7 @@ public class CapacityDaoImpl extends GenericDaoBase implements Map clusterCapacityMap = new HashMap(); StringBuilder sql = new StringBuilder(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART1); - + if(isZone){ sql.append("data_center_id = ?"); }else{ @@ -708,13 +706,13 @@ public class CapacityDaoImpl extends GenericDaoBase implements PreparedStatement pstmt = null; List result = new ArrayList(); Map podCapacityMap = new HashMap(); - + StringBuilder sql = new StringBuilder(ORDER_PODS_BY_AGGREGATE_CAPACITY); try { pstmt = txn.prepareAutoCloseStatement(sql.toString()); pstmt.setLong(2, zoneId); pstmt.setShort(3, capacityTypeForOrdering); - + if(capacityTypeForOrdering == CapacityVO.CAPACITY_TYPE_CPU){ pstmt.setFloat(1, cpuOverprovisioningFactor); pstmt.setFloat(4, cpuOverprovisioningFactor); @@ -722,7 +720,7 @@ public class CapacityDaoImpl extends GenericDaoBase implements pstmt.setFloat(1, 1); pstmt.setFloat(4, 1); } - + ResultSet rs = pstmt.executeQuery(); while (rs.next()) { Long podId = rs.getLong(1); @@ -736,13 +734,13 @@ public class CapacityDaoImpl extends GenericDaoBase implements throw new CloudRuntimeException("Caught: " + sql, e); } } - + @Override public void updateCapacityState(Long dcId, Long podId, Long clusterId, Long hostId, String capacityState) { Transaction txn = Transaction.currentTxn(); StringBuilder sql = new StringBuilder(UPDATE_CAPACITY_STATE); List resourceIdList = new ArrayList(); - + if (dcId != null){ sql.append(" data_center_id = ?"); resourceIdList.add(dcId); @@ -759,7 +757,7 @@ public class CapacityDaoImpl extends GenericDaoBase implements sql.append(" host_id = ?"); resourceIdList.add(hostId); } - + PreparedStatement pstmt = null; try { pstmt = txn.prepareAutoCloseStatement(sql.toString()); diff --git a/server/src/com/cloud/cluster/CheckPointManager.java b/server/src/com/cloud/cluster/CheckPointManager.java deleted file mode 100644 index b6333e6c4fa..00000000000 --- a/server/src/com/cloud/cluster/CheckPointManager.java +++ /dev/null @@ -1,52 +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. -package com.cloud.cluster; - - -/** - * TaskManager helps business logic deal with clustering failover. - * Say you're writing code that introduces an inconsistent state over - * of your operation? Who will come back to cleanup this state? TaskManager - * with different content during your process. If the server dies, TaskManager - * running elsewhere. If there are no clustered servers, then TaskManager will - * cleanup when the dead server resumes. - * - */ -public interface CheckPointManager { - /** - * responsible for cleaning up. - * - * @param context context information to be stored. - * @return Check point id. - */ - long pushCheckPoint(CleanupMaid context); - - /** - * update the task with new context - * @param taskId - * @param updatedContext new updated context. - */ - void updateCheckPointState(long taskId, CleanupMaid updatedContext); - - - /** - * removes the task as it is completed. - * - * @param taskId - */ - void popCheckPoint(long taskId); -} diff --git a/server/src/com/cloud/cluster/CheckPointManagerImpl.java b/server/src/com/cloud/cluster/CheckPointManagerImpl.java deleted file mode 100644 index e02a2f0dcc2..00000000000 --- a/server/src/com/cloud/cluster/CheckPointManagerImpl.java +++ /dev/null @@ -1,247 +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. -package com.cloud.cluster; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import javax.ejb.Local; -import javax.inject.Inject; -import javax.naming.ConfigurationException; - -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; - -import com.cloud.cluster.dao.StackMaidDao; -import com.cloud.configuration.Config; -import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.serializer.SerializerHelper; -import com.cloud.utils.DateUtil; -import com.cloud.utils.NumbersUtil; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.component.Manager; -import com.cloud.utils.concurrency.NamedThreadFactory; -import com.cloud.utils.db.DB; -import com.cloud.utils.db.GlobalLock; - -@Component -@Local(value=CheckPointManager.class) -public class CheckPointManagerImpl implements CheckPointManager, Manager, ClusterManagerListener { - private static final Logger s_logger = Logger.getLogger(CheckPointManagerImpl.class); - - private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 3; // 3 seconds - private int _cleanupRetryInterval; - - private String _name; - - @Inject - private StackMaidDao _maidDao; - - @Inject - private ClusterManager _clusterMgr; - - @Inject ConfigurationDao _configDao; - - long _msId; - - private final ScheduledExecutorService _cleanupScheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Task-Cleanup")); - - protected CheckPointManagerImpl() { - } - - @Override - public boolean configure(String name, Map xmlParams) throws ConfigurationException { - _name = name; - - if (s_logger.isInfoEnabled()) { - s_logger.info("Start configuring StackMaidManager : " + name); - } - - StackMaid.init(ManagementServerNode.getManagementServerId()); - _msId = ManagementServerNode.getManagementServerId(); - - _clusterMgr.registerListener(this); - - Map params = _configDao.getConfiguration(xmlParams); - - _cleanupRetryInterval = NumbersUtil.parseInt(params.get(Config.TaskCleanupRetryInterval.key()), 600); - _maidDao.takeover(_msId, _msId); - return true; - } - - private void cleanupLeftovers(List l) { - for (CheckPointVO maid : l) { - if (StackMaid.doCleanup(maid)) { - _maidDao.expunge(maid.getId()); - } - } - } - - @Override - public void onManagementNodeIsolated() { - } - - @DB - private Runnable getGCTask() { - return new Runnable() { - @Override - public void run() { - GlobalLock scanLock = GlobalLock.getInternLock("StackMaidManagerGC"); - try { - if (scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) { - try { - reallyRun(); - } finally { - scanLock.unlock(); - } - } - } finally { - scanLock.releaseRef(); - } - } - - public void reallyRun() { - try { - Date cutTime = new Date(DateUtil.currentGMTTime().getTime() - 7200000); - List l = _maidDao.listLeftoversByCutTime(cutTime); - cleanupLeftovers(l); - } catch (Throwable e) { - s_logger.error("Unexpected exception when trying to execute queue item, ", e); - } - } - }; - } - - @Override - public boolean start() { - _cleanupScheduler.schedule(new CleanupTask(), _cleanupRetryInterval > 0 ? _cleanupRetryInterval : 600, TimeUnit.SECONDS); - return true; - } - - @Override - public boolean stop() { - return true; - } - - @Override - public String getName() { - return _name; - } - - @Override - public void onManagementNodeJoined(List nodeList, long selfNodeId) { - // Nothing to do - } - - @Override - public void onManagementNodeLeft(List nodeList, long selfNodeId) { - for (ManagementServerHostVO node : nodeList) { - if (_maidDao.takeover(node.getMsid(), selfNodeId)) { - s_logger.info("Taking over from " + node.getMsid()); - _cleanupScheduler.execute(new CleanupTask()); - } - } - } - - @Override - @DB - public long pushCheckPoint(CleanupMaid context) { - long seq = _maidDao.pushCleanupDelegate(_msId, 0, context.getClass().getName(), context); - return seq; - } - - @Override - @DB - public void updateCheckPointState(long taskId, CleanupMaid updatedContext) { - CheckPointVO task = _maidDao.createForUpdate(); - task.setDelegate(updatedContext.getClass().getName()); - task.setContext(SerializerHelper.toSerializedStringOld(updatedContext)); - _maidDao.update(taskId, task); - } - - @Override - @DB - public void popCheckPoint(long taskId) { - _maidDao.remove(taskId); - } - - protected boolean cleanup(CheckPointVO task) { - s_logger.info("Cleaning up " + task); - CleanupMaid delegate = (CleanupMaid)SerializerHelper.fromSerializedString(task.getContext()); - assert delegate.getClass().getName().equals(task.getDelegate()) : "Deserializer says " + delegate.getClass().getName() + " but it's suppose to be " + task.getDelegate(); - - int result = delegate.cleanup(this); - if (result <= 0) { - if (result == 0) { - s_logger.info("Successfully cleaned up " + task.getId()); - } else { - s_logger.warn("Unsuccessful in cleaning up " + task + ". Procedure to cleanup manaully: " + delegate.getCleanupProcedure()); - } - popCheckPoint(task.getId()); - return true; - } else { - s_logger.error("Unable to cleanup " + task.getId()); - return false; - } - } - - class CleanupTask implements Runnable { - private Date _curDate; - public CleanupTask() { - _curDate = new Date(); - } - - @Override - public void run() { - try { - List tasks = _maidDao.listLeftoversByCutTime(_curDate, _msId); - tasks.addAll(_maidDao.listCleanupTasks(_msId)); - - List retries = new ArrayList(); - - for (CheckPointVO task : tasks) { - try { - if (!cleanup(task)) { - retries.add(task); - } - } catch (Exception e) { - s_logger.warn("Unable to clean up " + task, e); - - } - } - - if (retries.size() > 0) { - if (_cleanupRetryInterval > 0) { - _cleanupScheduler.schedule(this, _cleanupRetryInterval, TimeUnit.SECONDS); - } else { - for (CheckPointVO task : retries) { - s_logger.warn("Cleanup procedure for " + task + ": " + ((CleanupMaid)SerializerHelper.fromSerializedString(task.getContext())).getCleanupProcedure()); - } - } - } - - } catch (Exception e) { - s_logger.error("Unable to cleanup all of the tasks for " + _msId, e); - } - } - } -} diff --git a/server/src/com/cloud/cluster/CleanupMaid.java b/server/src/com/cloud/cluster/CleanupMaid.java deleted file mode 100644 index 1ff83f12286..00000000000 --- a/server/src/com/cloud/cluster/CleanupMaid.java +++ /dev/null @@ -1,41 +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. -package com.cloud.cluster; - -/** - * - * task. The state is serialized and stored. When cleanup is required - * CleanupMaid is instantiated from the stored data and cleanup() is called. - * - */ -public interface CleanupMaid { - /** - * cleanup according the state that was stored. - * - * @return 0 indicates cleanup was successful. Negative number - * indicates the cleanup was unsuccessful but don't retry. Positive number - * indicates the cleanup was unsuccessful and retry in this many seconds. - */ - int cleanup(CheckPointManager checkPointMgr); - - - /** - * returned here is recorded. - * @return - */ - String getCleanupProcedure(); -} diff --git a/server/src/com/cloud/cluster/ClusterManagerImpl.java b/server/src/com/cloud/cluster/ClusterManagerImpl.java index 013034f08a5..f71a8665951 100755 --- a/server/src/com/cloud/cluster/ClusterManagerImpl.java +++ b/server/src/com/cloud/cluster/ClusterManagerImpl.java @@ -29,7 +29,6 @@ import java.sql.SQLException; import java.sql.SQLRecoverableException; import java.util.ArrayList; import java.util.Date; -import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -75,8 +74,6 @@ import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Profiler; import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.component.Adapters; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.ConnectionConcierge; import com.cloud.utils.db.DB; @@ -123,7 +120,7 @@ public class ClusterManagerImpl implements ClusterManager { private final ExecutorService _executor; private ClusterServiceAdapter _currentServiceAdapter; - + @Inject private List _serviceAdapters; @@ -149,11 +146,11 @@ public class ClusterManagerImpl implements ClusterManager { private boolean _agentLBEnabled = false; private double _connectedAgentsThreshold = 0.7; private static boolean _agentLbHappened = false; - - private List _clusterPduOutgoingQueue = new ArrayList(); - private List _clusterPduIncomingQueue = new ArrayList(); - private Map _outgoingPdusWaitingForAck = new HashMap(); - + + private final List _clusterPduOutgoingQueue = new ArrayList(); + private final List _clusterPduIncomingQueue = new ArrayList(); + private final Map _outgoingPdusWaitingForAck = new HashMap(); + public ClusterManagerImpl() { _clusterPeers = new HashMap(); @@ -164,13 +161,13 @@ public class ClusterManagerImpl implements ClusterManager { // _executor = Executors.newCachedThreadPool(new NamedThreadFactory("Cluster-Worker")); } - + private void registerRequestPdu(ClusterServiceRequestPdu pdu) { synchronized(_outgoingPdusWaitingForAck) { _outgoingPdusWaitingForAck.put(pdu.getSequenceId(), pdu); } } - + private ClusterServiceRequestPdu popRequestPdu(long ackSequenceId) { synchronized(_outgoingPdusWaitingForAck) { if(_outgoingPdusWaitingForAck.get(ackSequenceId) != null) { @@ -179,10 +176,10 @@ public class ClusterManagerImpl implements ClusterManager { return pdu; } } - + return null; } - + private void cancelClusterRequestToPeer(String strPeer) { List candidates = new ArrayList(); synchronized(_outgoingPdusWaitingForAck) { @@ -195,7 +192,7 @@ public class ClusterManagerImpl implements ClusterManager { _outgoingPdusWaitingForAck.remove(pdu.getSequenceId()); } } - + for(ClusterServiceRequestPdu pdu : candidates) { s_logger.warn("Cancel cluster request PDU to peer: " + strPeer + ", pdu: " + _gson.toJson(pdu)); synchronized(pdu) { @@ -203,76 +200,78 @@ public class ClusterManagerImpl implements ClusterManager { } } } - + private void addOutgoingClusterPdu(ClusterServicePdu pdu) { - synchronized(_clusterPduOutgoingQueue) { - _clusterPduOutgoingQueue.add(pdu); - _clusterPduOutgoingQueue.notifyAll(); - } + synchronized(_clusterPduOutgoingQueue) { + _clusterPduOutgoingQueue.add(pdu); + _clusterPduOutgoingQueue.notifyAll(); + } } - + private ClusterServicePdu popOutgoingClusterPdu(long timeoutMs) { - synchronized(_clusterPduOutgoingQueue) { - try { - _clusterPduOutgoingQueue.wait(timeoutMs); - } catch (InterruptedException e) { - } - - if(_clusterPduOutgoingQueue.size() > 0) { - ClusterServicePdu pdu = _clusterPduOutgoingQueue.get(0); - _clusterPduOutgoingQueue.remove(0); - return pdu; - } - } - return null; + synchronized(_clusterPduOutgoingQueue) { + try { + _clusterPduOutgoingQueue.wait(timeoutMs); + } catch (InterruptedException e) { + } + + if(_clusterPduOutgoingQueue.size() > 0) { + ClusterServicePdu pdu = _clusterPduOutgoingQueue.get(0); + _clusterPduOutgoingQueue.remove(0); + return pdu; + } + } + return null; } private void addIncomingClusterPdu(ClusterServicePdu pdu) { - synchronized(_clusterPduIncomingQueue) { - _clusterPduIncomingQueue.add(pdu); - _clusterPduIncomingQueue.notifyAll(); - } + synchronized(_clusterPduIncomingQueue) { + _clusterPduIncomingQueue.add(pdu); + _clusterPduIncomingQueue.notifyAll(); + } } - + private ClusterServicePdu popIncomingClusterPdu(long timeoutMs) { - synchronized(_clusterPduIncomingQueue) { - try { - _clusterPduIncomingQueue.wait(timeoutMs); - } catch (InterruptedException e) { - } - - if(_clusterPduIncomingQueue.size() > 0) { - ClusterServicePdu pdu = _clusterPduIncomingQueue.get(0); - _clusterPduIncomingQueue.remove(0); - return pdu; - } - } - return null; + synchronized(_clusterPduIncomingQueue) { + try { + _clusterPduIncomingQueue.wait(timeoutMs); + } catch (InterruptedException e) { + } + + if(_clusterPduIncomingQueue.size() > 0) { + ClusterServicePdu pdu = _clusterPduIncomingQueue.get(0); + _clusterPduIncomingQueue.remove(0); + return pdu; + } + } + return null; } - + private Runnable getClusterPduSendingTask() { return new Runnable() { + @Override public void run() { onSendingClusterPdu(); } }; } - + private Runnable getClusterPduNotificationTask() { return new Runnable() { + @Override public void run() { onNotifyingClusterPdu(); } }; } - + private void onSendingClusterPdu() { while(true) { try { ClusterServicePdu pdu = popOutgoingClusterPdu(1000); if(pdu == null) - continue; - + continue; + ClusterService peerService = null; for(int i = 0; i < 2; i++) { try { @@ -285,20 +284,20 @@ public class ClusterManagerImpl implements ClusterManager { try { if(s_logger.isDebugEnabled()) { s_logger.debug("Cluster PDU " + getSelfPeerName() + " -> " + pdu.getDestPeer() + ". agent: " + pdu.getAgentId() - + ", pdu seq: " + pdu.getSequenceId() + ", pdu ack seq: " + pdu.getAckSequenceId() + ", json: " + pdu.getJsonPackage()); + + ", pdu seq: " + pdu.getSequenceId() + ", pdu ack seq: " + pdu.getAckSequenceId() + ", json: " + pdu.getJsonPackage()); } long startTick = System.currentTimeMillis(); String strResult = peerService.execute(pdu); if(s_logger.isDebugEnabled()) { s_logger.debug("Cluster PDU " + getSelfPeerName() + " -> " + pdu.getDestPeer() + " completed. time: " + - (System.currentTimeMillis() - startTick) + "ms. agent: " + pdu.getAgentId() - + ", pdu seq: " + pdu.getSequenceId() + ", pdu ack seq: " + pdu.getAckSequenceId() + ", json: " + pdu.getJsonPackage()); + (System.currentTimeMillis() - startTick) + "ms. agent: " + pdu.getAgentId() + + ", pdu seq: " + pdu.getSequenceId() + ", pdu ack seq: " + pdu.getAckSequenceId() + ", json: " + pdu.getJsonPackage()); } - + if("true".equals(strResult)) break; - + } catch (RemoteException e) { invalidatePeerService(pdu.getDestPeer()); if(s_logger.isInfoEnabled()) { @@ -313,50 +312,51 @@ public class ClusterManagerImpl implements ClusterManager { } } } - + private void onNotifyingClusterPdu() { while(true) { try { final ClusterServicePdu pdu = popIncomingClusterPdu(1000); if(pdu == null) - continue; + continue; _executor.execute(new Runnable() { - public void run() { - if(pdu.getPduType() == ClusterServicePdu.PDU_TYPE_RESPONSE) { - ClusterServiceRequestPdu requestPdu = popRequestPdu(pdu.getAckSequenceId()); - if(requestPdu != null) { - requestPdu.setResponseResult(pdu.getJsonPackage()); - synchronized(requestPdu) { - requestPdu.notifyAll(); - } - } else { - s_logger.warn("Original request has already been cancelled. pdu: " + _gson.toJson(pdu)); - } - } else { - String result = dispatchClusterServicePdu(pdu); - if(result == null) - result = ""; - - if(pdu.getPduType() == ClusterServicePdu.PDU_TYPE_REQUEST) { - ClusterServicePdu responsePdu = new ClusterServicePdu(); - responsePdu.setPduType(ClusterServicePdu.PDU_TYPE_RESPONSE); - responsePdu.setSourcePeer(pdu.getDestPeer()); - responsePdu.setDestPeer(pdu.getSourcePeer()); - responsePdu.setAckSequenceId(pdu.getSequenceId()); - responsePdu.setJsonPackage(result); - - addOutgoingClusterPdu(responsePdu); - } - } - } + @Override + public void run() { + if(pdu.getPduType() == ClusterServicePdu.PDU_TYPE_RESPONSE) { + ClusterServiceRequestPdu requestPdu = popRequestPdu(pdu.getAckSequenceId()); + if(requestPdu != null) { + requestPdu.setResponseResult(pdu.getJsonPackage()); + synchronized(requestPdu) { + requestPdu.notifyAll(); + } + } else { + s_logger.warn("Original request has already been cancelled. pdu: " + _gson.toJson(pdu)); + } + } else { + String result = dispatchClusterServicePdu(pdu); + if(result == null) + result = ""; + + if(pdu.getPduType() == ClusterServicePdu.PDU_TYPE_REQUEST) { + ClusterServicePdu responsePdu = new ClusterServicePdu(); + responsePdu.setPduType(ClusterServicePdu.PDU_TYPE_RESPONSE); + responsePdu.setSourcePeer(pdu.getDestPeer()); + responsePdu.setDestPeer(pdu.getSourcePeer()); + responsePdu.setAckSequenceId(pdu.getSequenceId()); + responsePdu.setJsonPackage(result); + + addOutgoingClusterPdu(responsePdu); + } + } + } }); } catch(Throwable e) { s_logger.error("Unexcpeted exception: ", e); } } } - + private String dispatchClusterServicePdu(ClusterServicePdu pdu) { if(s_logger.isDebugEnabled()) { @@ -370,7 +370,7 @@ public class ClusterManagerImpl implements ClusterManager { assert(false); s_logger.error("Excection in gson decoding : ", e); } - + if (cmds.length == 1 && cmds[0] instanceof ChangeAgentCommand) { //intercepted ChangeAgentCommand cmd = (ChangeAgentCommand)cmds[0]; @@ -416,22 +416,22 @@ public class ClusterManagerImpl implements ClusterManager { answers[0] = new Answer(cmd, result, null); return _gson.toJson(answers); } else if (cmds.length == 1 && cmds[0] instanceof PropagateResourceEventCommand ) { - PropagateResourceEventCommand cmd = (PropagateResourceEventCommand) cmds[0]; - - s_logger.debug("Intercepting command to propagate event " + cmd.getEvent().name() + " for host " + cmd.getHostId()); - - boolean result = false; - try { - result = executeResourceUserRequest(cmd.getHostId(), cmd.getEvent()); - s_logger.debug("Result is " + result); - } catch (AgentUnavailableException ex) { - s_logger.warn("Agent is unavailable", ex); - return null; - } - - Answer[] answers = new Answer[1]; - answers[0] = new Answer(cmd, result, null); - return _gson.toJson(answers); + PropagateResourceEventCommand cmd = (PropagateResourceEventCommand) cmds[0]; + + s_logger.debug("Intercepting command to propagate event " + cmd.getEvent().name() + " for host " + cmd.getHostId()); + + boolean result = false; + try { + result = executeResourceUserRequest(cmd.getHostId(), cmd.getEvent()); + s_logger.debug("Result is " + result); + } catch (AgentUnavailableException ex) { + s_logger.warn("Agent is unavailable", ex); + return null; + } + + Answer[] answers = new Answer[1]; + answers[0] = new Answer(cmd, result, null); + return _gson.toJson(answers); } try { @@ -461,14 +461,15 @@ public class ClusterManagerImpl implements ClusterManager { } catch (OperationTimedoutException e) { s_logger.warn("Timed Out", e); } - + return null; } + @Override public void OnReceiveClusterServicePdu(ClusterServicePdu pdu) { - addIncomingClusterPdu(pdu); + addIncomingClusterPdu(pdu); } - + @Override public Answer[] sendToAgent(Long hostId, Command[] cmds, boolean stopOnError) throws AgentUnavailableException, OperationTimedoutException { Commands commands = new Commands(stopOnError ? OnError.Stop : OnError.Continue); @@ -558,7 +559,7 @@ public class ClusterManagerImpl implements ClusterManager { s_logger.debug(getSelfPeerName() + " -> " + strPeer + "." + agentId + " " + _gson.toJson(cmds, Command[].class)); } - + ClusterServiceRequestPdu pdu = new ClusterServiceRequestPdu(); pdu.setSourcePeer(getSelfPeerName()); pdu.setDestPeer(strPeer); @@ -567,7 +568,7 @@ public class ClusterManagerImpl implements ClusterManager { pdu.setStopOnError(stopOnError); registerRequestPdu(pdu); addOutgoingClusterPdu(pdu); - + synchronized(pdu) { try { pdu.wait(); @@ -577,9 +578,9 @@ public class ClusterManagerImpl implements ClusterManager { if(s_logger.isDebugEnabled()) { s_logger.debug(getSelfPeerName() + " -> " + strPeer + "." + agentId + " completed. result: " + - pdu.getResponseResult()); + pdu.getResponseResult()); } - + if(pdu.getResponseResult() != null && pdu.getResponseResult().length() > 0) { try { return _gson.fromJson(pdu.getResponseResult(), Answer[].class); @@ -590,7 +591,7 @@ public class ClusterManagerImpl implements ClusterManager { return null; } - + @Override public String getPeerName(long agentHostId) { @@ -625,18 +626,18 @@ public class ClusterManagerImpl implements ClusterManager { // Note : we don't check duplicates synchronized (_listeners) { - s_logger.info("register cluster listener " + listener.getClass()); - - _listeners.add(listener); + s_logger.info("register cluster listener " + listener.getClass()); + + _listeners.add(listener); } } @Override public void unregisterListener(ClusterManagerListener listener) { synchronized(_listeners) { - s_logger.info("unregister cluster listener " + listener.getClass()); - - _listeners.remove(listener); + s_logger.info("unregister cluster listener " + listener.getClass()); + + _listeners.remove(listener); } } @@ -663,7 +664,7 @@ public class ClusterManagerImpl implements ClusterManager { if(s_logger.isDebugEnabled()) { s_logger.debug("Notify management server node left to listeners."); } - + for(ManagementServerHostVO mshost : nodeList) { if(s_logger.isDebugEnabled()) s_logger.debug("Leaving node, IP: " + mshost.getServiceIP() + ", msid: " + mshost.getMsid()); @@ -731,32 +732,32 @@ public class ClusterManagerImpl implements ClusterManager { Profiler profilerHeartbeatUpdate = new Profiler(); Profiler profilerPeerScan = new Profiler(); Profiler profilerAgentLB = new Profiler(); - + try { profiler.start(); - + profilerHeartbeatUpdate.start(); txn.transitToUserManagedConnection(getHeartbeatConnection()); if(s_logger.isTraceEnabled()) { s_logger.trace("Cluster manager heartbeat update, id:" + _mshostId); } - + _mshostDao.update(_mshostId, getCurrentRunId(), DateUtil.currentGMTTime()); profilerHeartbeatUpdate.stop(); - + profilerPeerScan.start(); if (s_logger.isTraceEnabled()) { s_logger.trace("Cluster manager peer-scan, id:" + _mshostId); } - + if (!_peerScanInited) { _peerScanInited = true; initPeerScan(); } - + peerScan(); profilerPeerScan.stop(); - + profilerAgentLB.start(); //initiate agent lb task will be scheduled and executed only once, and only when number of agents loaded exceeds _connectedAgentsThreshold if (_agentLBEnabled && !_agentLbHappened) { @@ -764,7 +765,7 @@ public class ClusterManagerImpl implements ClusterManager { sc.addAnd(sc.getEntity().getManagementServerId(), Op.NNULL); sc.addAnd(sc.getEntity().getType(), Op.EQ, Host.Type.Routing); List allManagedRoutingAgents = sc.list(); - + sc = SearchCriteria2.create(HostVO.class); sc.addAnd(sc.getEntity().getType(), Op.EQ, Host.Type.Routing); List allAgents = sc.list(); @@ -784,16 +785,16 @@ public class ClusterManagerImpl implements ClusterManager { profilerAgentLB.stop(); } finally { profiler.stop(); - + if(profiler.getDuration() >= _heartbeatInterval) { if(s_logger.isDebugEnabled()) s_logger.debug("Management server heartbeat takes too long to finish. profiler: " + profiler.toString() + - ", profilerHeartbeatUpdate: " + profilerHeartbeatUpdate.toString() + - ", profilerPeerScan: " + profilerPeerScan.toString() + - ", profilerAgentLB: " + profilerAgentLB.toString()); + ", profilerHeartbeatUpdate: " + profilerHeartbeatUpdate.toString() + + ", profilerPeerScan: " + profilerPeerScan.toString() + + ", profilerAgentLB: " + profilerAgentLB.toString()); } } - + } catch(CloudRuntimeException e) { s_logger.error("Runtime DB exception ", e.getCause()); @@ -933,33 +934,33 @@ public class ClusterManagerImpl implements ClusterManager { this._notificationMsgs.add(msg); this._notificationMsgs.notifyAll(); } - + switch(msg.getMessageType()) { case nodeAdded: - { - List l = msg.getNodes(); - if(l != null && l.size() > 0) { - for(ManagementServerHostVO mshost: l) { - _mshostPeerDao.updatePeerInfo(_mshostId, mshost.getId(), mshost.getRunid(), ManagementServerHost.State.Up); - } + { + List l = msg.getNodes(); + if(l != null && l.size() > 0) { + for(ManagementServerHostVO mshost: l) { + _mshostPeerDao.updatePeerInfo(_mshostId, mshost.getId(), mshost.getRunid(), ManagementServerHost.State.Up); } } - break; - + } + break; + case nodeRemoved: - { - List l = msg.getNodes(); - if(l != null && l.size() > 0) { - for(ManagementServerHostVO mshost: l) { - _mshostPeerDao.updatePeerInfo(_mshostId, mshost.getId(), mshost.getRunid(), ManagementServerHost.State.Down); - } + { + List l = msg.getNodes(); + if(l != null && l.size() > 0) { + for(ManagementServerHostVO mshost: l) { + _mshostPeerDao.updatePeerInfo(_mshostId, mshost.getId(), mshost.getRunid(), ManagementServerHost.State.Down); } } - break; - + } + break; + default : break; - + } } @@ -978,39 +979,39 @@ public class ClusterManagerImpl implements ClusterManager { // missed cleanup Date cutTime = DateUtil.currentGMTTime(); List inactiveList = _mshostDao.getInactiveList(new Date(cutTime.getTime() - _heartbeatThreshold)); - + // We don't have foreign key constraints to enforce the mgmt_server_id integrity in host table, when user manually // remove records from mshost table, this will leave orphan mgmt_serve_id reference in host table. List orphanList = _mshostDao.listOrphanMsids(); if(orphanList.size() > 0) { - for(Long orphanMsid : orphanList) { - // construct fake ManagementServerHostVO based on orphan MSID - s_logger.info("Add orphan management server msid found in host table to initial clustering notification, orphan msid: " + orphanMsid); - inactiveList.add(new ManagementServerHostVO(orphanMsid, 0, "orphan", 0, new Date())); - } - } else { - s_logger.info("We are good, no orphan management server msid in host table is found"); - } - - if(inactiveList.size() > 0) { - if(s_logger.isInfoEnabled()) { - s_logger.info("Found " + inactiveList.size() + " inactive management server node based on timestamp"); - for(ManagementServerHostVO host : inactiveList) - s_logger.info("management server node msid: " + host.getMsid() + ", name: " + host.getName() + ", service ip: " + host.getServiceIP() + ", version: " + host.getVersion()); - } - - List downHostList = new ArrayList(); - for(ManagementServerHostVO host : inactiveList) { - if(!pingManagementNode(host)) { - s_logger.warn("Management node " + host.getId() + " is detected inactive by timestamp and also not pingable"); - downHostList.add(host); - } + for(Long orphanMsid : orphanList) { + // construct fake ManagementServerHostVO based on orphan MSID + s_logger.info("Add orphan management server msid found in host table to initial clustering notification, orphan msid: " + orphanMsid); + inactiveList.add(new ManagementServerHostVO(orphanMsid, 0, "orphan", 0, new Date())); } - - if(downHostList.size() > 0) - this.queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeRemoved, downHostList)); } else { - s_logger.info("No inactive management server node found"); + s_logger.info("We are good, no orphan management server msid in host table is found"); + } + + if(inactiveList.size() > 0) { + if(s_logger.isInfoEnabled()) { + s_logger.info("Found " + inactiveList.size() + " inactive management server node based on timestamp"); + for(ManagementServerHostVO host : inactiveList) + s_logger.info("management server node msid: " + host.getMsid() + ", name: " + host.getName() + ", service ip: " + host.getServiceIP() + ", version: " + host.getVersion()); + } + + List downHostList = new ArrayList(); + for(ManagementServerHostVO host : inactiveList) { + if(!pingManagementNode(host)) { + s_logger.warn("Management node " + host.getId() + " is detected inactive by timestamp and also not pingable"); + downHostList.add(host); + } + } + + if(downHostList.size() > 0) + this.queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeRemoved, downHostList)); + } else { + s_logger.info("No inactive management server node found"); } } @@ -1019,7 +1020,7 @@ public class ClusterManagerImpl implements ClusterManager { Profiler profiler = new Profiler(); profiler.start(); - + Profiler profilerQueryActiveList = new Profiler(); profilerQueryActiveList.start(); List currentList = _mshostDao.getActiveList(new Date(cutTime.getTime() - _heartbeatThreshold)); @@ -1031,13 +1032,13 @@ public class ClusterManagerImpl implements ClusterManager { List invalidatedNodeList = new ArrayList(); if(_mshostId != null) { - + if(_mshostPeerDao.countStateSeenInPeers(_mshostId, _runId, ManagementServerHost.State.Down) > 0) { String msg = "We have detected that at least one management server peer reports that this management server is down, perform active fencing to avoid split-brain situation"; s_logger.error(msg); throw new ActiveFencingException(msg); } - + // only if we have already attached to cluster, will we start to check leaving nodes for(Map.Entry entry : _activePeers.entrySet()) { @@ -1070,7 +1071,7 @@ public class ClusterManagerImpl implements ClusterManager { } } profilerSyncClusterInfo.stop(); - + Profiler profilerInvalidatedNodeList = new Profiler(); profilerInvalidatedNodeList.start(); // process invalidated node list @@ -1134,16 +1135,16 @@ public class ClusterManagerImpl implements ClusterManager { if(newNodeList.size() > 0) { this.queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeAdded, newNodeList)); } - + profiler.stop(); - + if(profiler.getDuration() >= this._heartbeatInterval) { if(s_logger.isDebugEnabled()) s_logger.debug("Peer scan takes too long to finish. profiler: " + profiler.toString() - + ", profilerQueryActiveList: " + profilerQueryActiveList.toString() - + ", profilerSyncClusterInfo: " + profilerSyncClusterInfo.toString() - + ", profilerInvalidatedNodeList: " + profilerInvalidatedNodeList.toString() - + ", profilerRemovedList: " + profilerRemovedList.toString()); + + ", profilerQueryActiveList: " + profilerQueryActiveList.toString() + + ", profilerSyncClusterInfo: " + profilerSyncClusterInfo.toString() + + ", profilerInvalidatedNodeList: " + profilerInvalidatedNodeList.toString() + + ", profilerRemovedList: " + profilerRemovedList.toString()); } } @@ -1206,7 +1207,7 @@ public class ClusterManagerImpl implements ClusterManager { if (s_logger.isInfoEnabled()) { s_logger.info("Management server (host id : " + _mshostId + ") is being started at " + _clusterNodeIP + ":" + _currentServiceAdapter.getServicePort()); } - + _mshostPeerDao.clearPeerInfo(_mshostId); // use seperate thread for heartbeat updates @@ -1294,8 +1295,8 @@ public class ClusterManagerImpl implements ClusterManager { } for(int i = 0; i < DEFAULT_OUTGOING_WORKERS; i++) - _executor.execute(getClusterPduSendingTask()); - + _executor.execute(getClusterPduSendingTask()); + // notification task itself in turn works as a task dispatcher _executor.execute(getClusterPduNotificationTask()); @@ -1309,9 +1310,9 @@ public class ClusterManagerImpl implements ClusterManager { } _agentLBEnabled = Boolean.valueOf(_configDao.getValue(Config.AgentLbEnable.key())); - + String connectedAgentsThreshold = configs.get("agent.load.threshold"); - + if (connectedAgentsThreshold != null) { _connectedAgentsThreshold = Double.parseDouble(connectedAgentsThreshold); } @@ -1365,7 +1366,7 @@ public class ClusterManagerImpl implements ClusterManager { s_logger.info("ping management node cluster service can not be performed on self"); return false; } - + int retry = 10; while (--retry > 0) { SocketChannel sch = null; @@ -1381,7 +1382,7 @@ public class ClusterManagerImpl implements ClusterManager { } catch (IOException e) { if (e instanceof ConnectException) { s_logger.error("Unable to ping management server at " + targetIp + ":" + mshost.getServicePort() + " due to ConnectException", e); - return false; + return false; } } finally { if (sch != null) { @@ -1397,7 +1398,7 @@ public class ClusterManagerImpl implements ClusterManager { } catch (InterruptedException ex) { } } - + s_logger.error("Unable to ping management server at " + targetIp + ":" + mshost.getServicePort() + " after retries"); return false; } @@ -1455,7 +1456,7 @@ public class ClusterManagerImpl implements ClusterManager { public boolean isAgentRebalanceEnabled() { return _agentLBEnabled; } - + @Override public Boolean propagateResourceEvent(long agentId, ResourceState.Event event) throws AgentUnavailableException { final String msPeer = getPeerName(agentId); @@ -1480,7 +1481,7 @@ public class ClusterManagerImpl implements ClusterManager { return answers[0].getResult(); } - + @Override public boolean executeResourceUserRequest(long hostId, ResourceState.Event event) throws AgentUnavailableException { return _resourceMgr.executeUserRequest(hostId, event); diff --git a/server/src/com/cloud/cluster/StackMaid.java b/server/src/com/cloud/cluster/StackMaid.java deleted file mode 100644 index b84d73d4219..00000000000 --- a/server/src/com/cloud/cluster/StackMaid.java +++ /dev/null @@ -1,153 +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. -package com.cloud.cluster; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.log4j.Logger; - -import com.cloud.cluster.dao.StackMaidDao; -import com.cloud.cluster.dao.StackMaidDaoImpl; -import com.cloud.serializer.SerializerHelper; -import com.cloud.utils.CleanupDelegate; -import com.cloud.utils.db.Transaction; - -public class StackMaid { - protected final static Logger s_logger = Logger.getLogger(StackMaid.class); - - private static ThreadLocal threadMaid = new ThreadLocal(); - - private static long msid_setby_manager = 0; - - private StackMaidDao maidDao = new StackMaidDaoImpl(); - private int currentSeq = 0; - private Map context = new HashMap(); - - public static void init(long msid) { - msid_setby_manager = msid; - } - - public static StackMaid current() { - StackMaid maid = threadMaid.get(); - if(maid == null) { - maid = new StackMaid(); - threadMaid.set(maid); - } - return maid; - } - - public void registerContext(String key, Object contextObject) { - assert(!context.containsKey(key)) : "Context key has already been registered"; - context.put(key, contextObject); - } - - public Object getContext(String key) { - return context.get(key); - } - - public void expungeMaidItem(long maidId) { - // this is a bit ugly, but when it is not loaded by component locator, this is just a workable way for now - Transaction txn = Transaction.open(Transaction.CLOUD_DB); - try { - maidDao.expunge(maidId); - } finally { - txn.close(); - } - } - - public int push(String delegateClzName, Object context) { - assert(msid_setby_manager != 0) : "Fatal, make sure StackMaidManager is loaded"; - if(msid_setby_manager == 0) - s_logger.error("Fatal, make sure StackMaidManager is loaded"); - - return push(msid_setby_manager, delegateClzName, context); - } - - public int push(long currentMsid, String delegateClzName, Object context) { - int savePoint = currentSeq; - maidDao.pushCleanupDelegate(currentMsid, currentSeq++, delegateClzName, context); - return savePoint; - } - - public void pop(int savePoint) { - assert(msid_setby_manager != 0) : "Fatal, make sure StackMaidManager is loaded"; - if(msid_setby_manager == 0) - s_logger.error("Fatal, make sure StackMaidManager is loaded"); - - pop(msid_setby_manager, savePoint); - } - - public void pop() { - if(currentSeq > 0) - pop(currentSeq -1); - } - - /** - * must be called within thread context - * @param currentMsid - */ - public void pop(long currentMsid, int savePoint) { - while(currentSeq > savePoint) { - maidDao.popCleanupDelegate(currentMsid); - currentSeq--; - } - } - - public void exitCleanup() { - exitCleanup(msid_setby_manager); - } - - public void exitCleanup(long currentMsid) { - if(currentSeq > 0) { - CheckPointVO maid = null; - while((maid = maidDao.popCleanupDelegate(currentMsid)) != null) { - doCleanup(maid); - } - currentSeq = 0; - } - - context.clear(); - } - - public static boolean doCleanup(CheckPointVO maid) { - if(maid.getDelegate() != null) { - try { - Class clz = Class.forName(maid.getDelegate()); - Object delegate = clz.newInstance(); - if(delegate instanceof CleanupDelegate) { - return ((CleanupDelegate)delegate).cleanup(SerializerHelper.fromSerializedString(maid.getContext()), maid); - } else { - assert(false); - } - } catch (final ClassNotFoundException e) { - s_logger.error("Unable to load StackMaid delegate class: " + maid.getDelegate(), e); - } catch (final SecurityException e) { - s_logger.error("Security excetion when loading resource: " + maid.getDelegate()); - } catch (final IllegalArgumentException e) { - s_logger.error("Illegal argument excetion when loading resource: " + maid.getDelegate()); - } catch (final InstantiationException e) { - s_logger.error("Instantiation excetion when loading resource: " + maid.getDelegate()); - } catch (final IllegalAccessException e) { - s_logger.error("Illegal access exception when loading resource: " + maid.getDelegate()); - } - - return false; - } - return true; - } -} diff --git a/server/src/com/cloud/configuration/dao/ResourceCountDaoImpl.java b/server/src/com/cloud/configuration/dao/ResourceCountDaoImpl.java index 2c550eaaeba..26121920569 100644 --- a/server/src/com/cloud/configuration/dao/ResourceCountDaoImpl.java +++ b/server/src/com/cloud/configuration/dao/ResourceCountDaoImpl.java @@ -34,7 +34,6 @@ import com.cloud.configuration.ResourceLimit; import com.cloud.domain.dao.DomainDaoImpl; import com.cloud.exception.UnsupportedServiceException; import com.cloud.user.dao.AccountDaoImpl; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; @@ -44,90 +43,90 @@ import com.cloud.utils.db.Transaction; @Component @Local(value={ResourceCountDao.class}) public class ResourceCountDaoImpl extends GenericDaoBase implements ResourceCountDao { - private SearchBuilder TypeSearch; - - private SearchBuilder AccountSearch; - private SearchBuilder DomainSearch; - - //protected final DomainDaoImpl _domainDao = ComponentLocator.inject(DomainDaoImpl.class); - //protected final AccountDaoImpl _accountDao = ComponentLocator.inject(AccountDaoImpl.class); + private final SearchBuilder TypeSearch; - @Inject protected DomainDaoImpl _domainDao; - @Inject protected AccountDaoImpl _accountDao; + private final SearchBuilder AccountSearch; + private final SearchBuilder DomainSearch; - public ResourceCountDaoImpl() { - TypeSearch = createSearchBuilder(); - TypeSearch.and("type", TypeSearch.entity().getType(), SearchCriteria.Op.EQ); - TypeSearch.and("accountId", TypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ); - TypeSearch.and("domainId", TypeSearch.entity().getDomainId(), SearchCriteria.Op.EQ); - TypeSearch.done(); - - AccountSearch = createSearchBuilder(); - AccountSearch.and("accountId", AccountSearch.entity().getAccountId(), SearchCriteria.Op.NNULL); - AccountSearch.done(); - - DomainSearch = createSearchBuilder(); - DomainSearch.and("domainId", DomainSearch.entity().getDomainId(), SearchCriteria.Op.NNULL); - DomainSearch.done(); - } - - @Override - public ResourceCountVO findByOwnerAndType(long ownerId, ResourceOwnerType ownerType, ResourceType type) { - SearchCriteria sc = TypeSearch.create(); - sc.setParameters("type", type); - - if (ownerType == ResourceOwnerType.Account) { - sc.setParameters("accountId", ownerId); - return findOneIncludingRemovedBy(sc); - } else if (ownerType == ResourceOwnerType.Domain) { - sc.setParameters("domainId", ownerId); + //protected final DomainDaoImpl _domainDao = ComponentLocator.inject(DomainDaoImpl.class); + //protected final AccountDaoImpl _accountDao = ComponentLocator.inject(AccountDaoImpl.class); + + @Inject protected DomainDaoImpl _domainDao; + @Inject protected AccountDaoImpl _accountDao; + + public ResourceCountDaoImpl() { + TypeSearch = createSearchBuilder(); + TypeSearch.and("type", TypeSearch.entity().getType(), SearchCriteria.Op.EQ); + TypeSearch.and("accountId", TypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + TypeSearch.and("domainId", TypeSearch.entity().getDomainId(), SearchCriteria.Op.EQ); + TypeSearch.done(); + + AccountSearch = createSearchBuilder(); + AccountSearch.and("accountId", AccountSearch.entity().getAccountId(), SearchCriteria.Op.NNULL); + AccountSearch.done(); + + DomainSearch = createSearchBuilder(); + DomainSearch.and("domainId", DomainSearch.entity().getDomainId(), SearchCriteria.Op.NNULL); + DomainSearch.done(); + } + + @Override + public ResourceCountVO findByOwnerAndType(long ownerId, ResourceOwnerType ownerType, ResourceType type) { + SearchCriteria sc = TypeSearch.create(); + sc.setParameters("type", type); + + if (ownerType == ResourceOwnerType.Account) { + sc.setParameters("accountId", ownerId); return findOneIncludingRemovedBy(sc); - } else { - return null; - } - } - - @Override - public long getResourceCount(long ownerId, ResourceOwnerType ownerType, ResourceType type) { - ResourceCountVO vo = findByOwnerAndType(ownerId, ownerType, type); - if (vo != null) { - return vo.getCount(); - } else { - return 0; - } - } - - @Override - public void setResourceCount(long ownerId, ResourceOwnerType ownerType, ResourceType type, long count) { - ResourceCountVO resourceCountVO = findByOwnerAndType(ownerId, ownerType, type); + } else if (ownerType == ResourceOwnerType.Domain) { + sc.setParameters("domainId", ownerId); + return findOneIncludingRemovedBy(sc); + } else { + return null; + } + } + + @Override + public long getResourceCount(long ownerId, ResourceOwnerType ownerType, ResourceType type) { + ResourceCountVO vo = findByOwnerAndType(ownerId, ownerType, type); + if (vo != null) { + return vo.getCount(); + } else { + return 0; + } + } + + @Override + public void setResourceCount(long ownerId, ResourceOwnerType ownerType, ResourceType type, long count) { + ResourceCountVO resourceCountVO = findByOwnerAndType(ownerId, ownerType, type); if (count != resourceCountVO.getCount()) { resourceCountVO.setCount(count); update(resourceCountVO.getId(), resourceCountVO); } - } + } - @Override @Deprecated - public void updateDomainCount(long domainId, ResourceType type, boolean increment, long delta) { - delta = increment ? delta : delta * -1; + @Override @Deprecated + public void updateDomainCount(long domainId, ResourceType type, boolean increment, long delta) { + delta = increment ? delta : delta * -1; ResourceCountVO resourceCountVO = findByOwnerAndType(domainId, ResourceOwnerType.Domain, type); - resourceCountVO.setCount(resourceCountVO.getCount() + delta); - update(resourceCountVO.getId(), resourceCountVO); - } - - @Override - public boolean updateById(long id, boolean increment, long delta) { - delta = increment ? delta : delta * -1; - - ResourceCountVO resourceCountVO = findById(id); - resourceCountVO.setCount(resourceCountVO.getCount() + delta); - return update(resourceCountVO.getId(), resourceCountVO); - } - - @Override - public Set listRowsToUpdateForDomain(long domainId, ResourceType type) { - Set rowIds = new HashSet(); - Set domainIdsToUpdate = _domainDao.getDomainParentIds(domainId); + resourceCountVO.setCount(resourceCountVO.getCount() + delta); + update(resourceCountVO.getId(), resourceCountVO); + } + + @Override + public boolean updateById(long id, boolean increment, long delta) { + delta = increment ? delta : delta * -1; + + ResourceCountVO resourceCountVO = findById(id); + resourceCountVO.setCount(resourceCountVO.getCount() + delta); + return update(resourceCountVO.getId(), resourceCountVO); + } + + @Override + public Set listRowsToUpdateForDomain(long domainId, ResourceType type) { + Set rowIds = new HashSet(); + Set domainIdsToUpdate = _domainDao.getDomainParentIds(domainId); for (Long domainIdToUpdate : domainIdsToUpdate) { ResourceCountVO domainCountRecord = findByOwnerAndType(domainIdToUpdate, ResourceOwnerType.Domain, type); if (domainCountRecord != null) { @@ -135,34 +134,34 @@ public class ResourceCountDaoImpl extends GenericDaoBase } } return rowIds; - } - - @Override - public Set listAllRowsToUpdate(long ownerId, ResourceOwnerType ownerType, ResourceType type) { - Set rowIds = new HashSet(); - - if (ownerType == ResourceOwnerType.Account) { - //get records for account - ResourceCountVO accountCountRecord = findByOwnerAndType(ownerId, ResourceOwnerType.Account, type); - if (accountCountRecord != null) { - rowIds.add(accountCountRecord.getId()); - } - - //get records for account's domain and all its parent domains - rowIds.addAll(listRowsToUpdateForDomain(_accountDao.findByIdIncludingRemoved(ownerId).getDomainId(),type)); - } else if (ownerType == ResourceOwnerType.Domain) { - return listRowsToUpdateForDomain(ownerId, type); - } - - return rowIds; - } - - @Override @DB + } + + @Override + public Set listAllRowsToUpdate(long ownerId, ResourceOwnerType ownerType, ResourceType type) { + Set rowIds = new HashSet(); + + if (ownerType == ResourceOwnerType.Account) { + //get records for account + ResourceCountVO accountCountRecord = findByOwnerAndType(ownerId, ResourceOwnerType.Account, type); + if (accountCountRecord != null) { + rowIds.add(accountCountRecord.getId()); + } + + //get records for account's domain and all its parent domains + rowIds.addAll(listRowsToUpdateForDomain(_accountDao.findByIdIncludingRemoved(ownerId).getDomainId(),type)); + } else if (ownerType == ResourceOwnerType.Domain) { + return listRowsToUpdateForDomain(ownerId, type); + } + + return rowIds; + } + + @Override @DB public void createResourceCounts(long ownerId, ResourceLimit.ResourceOwnerType ownerType){ - + Transaction txn = Transaction.currentTxn(); txn.start(); - + ResourceType[] resourceTypes = Resource.ResourceType.values(); for (ResourceType resourceType : resourceTypes) { if (!resourceType.supportsOwner(ownerType)) { @@ -171,24 +170,24 @@ public class ResourceCountDaoImpl extends GenericDaoBase ResourceCountVO resourceCountVO = new ResourceCountVO(resourceType, 0, ownerId, ownerType); persist(resourceCountVO); } - + txn.commit(); } - - private List listByDomainId(long domainId) { - SearchCriteria sc = TypeSearch.create(); + + private List listByDomainId(long domainId) { + SearchCriteria sc = TypeSearch.create(); sc.setParameters("domainId", domainId); return listBy(sc); - } - + } + private List listByAccountId(long accountId) { - SearchCriteria sc = TypeSearch.create(); + SearchCriteria sc = TypeSearch.create(); sc.setParameters("accountId", accountId); return listBy(sc); - } - + } + @Override public List listByOwnerId(long ownerId, ResourceOwnerType ownerType) { if (ownerType == ResourceOwnerType.Account) { @@ -199,26 +198,26 @@ public class ResourceCountDaoImpl extends GenericDaoBase return new ArrayList(); } } - - @Override - public List listResourceCountByOwnerType(ResourceOwnerType ownerType) { - if (ownerType == ResourceOwnerType.Account) { - return listBy(AccountSearch.create()); - } else if (ownerType == ResourceOwnerType.Domain) { - return listBy(DomainSearch.create()); - } else { - return new ArrayList(); - } - } - - @Override + + @Override + public List listResourceCountByOwnerType(ResourceOwnerType ownerType) { + if (ownerType == ResourceOwnerType.Account) { + return listBy(AccountSearch.create()); + } else if (ownerType == ResourceOwnerType.Domain) { + return listBy(DomainSearch.create()); + } else { + return new ArrayList(); + } + } + + @Override public ResourceCountVO persist(ResourceCountVO resourceCountVO){ - ResourceOwnerType ownerType = resourceCountVO.getResourceOwnerType(); - ResourceType resourceType = resourceCountVO.getType(); - if (!resourceType.supportsOwner(ownerType)) { - throw new UnsupportedServiceException("Resource type " + resourceType + " is not supported for owner of type " + ownerType.getName()); - } - + ResourceOwnerType ownerType = resourceCountVO.getResourceOwnerType(); + ResourceType resourceType = resourceCountVO.getType(); + if (!resourceType.supportsOwner(ownerType)) { + throw new UnsupportedServiceException("Resource type " + resourceType + " is not supported for owner of type " + ownerType.getName()); + } + return super.persist(resourceCountVO); } } \ No newline at end of file diff --git a/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java b/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java index a3de9466dab..a2399977ab0 100755 --- a/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java +++ b/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java @@ -49,7 +49,6 @@ import com.cloud.host.dao.HostDao; import com.cloud.info.ConsoleProxyInfo; import com.cloud.network.Network; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.component.ComponentLocator; import com.cloud.vm.ConsoleProxyVO; import com.cloud.vm.ReservationContext; import com.cloud.vm.UserVmVO; @@ -87,9 +86,9 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu VirtualMachineManager _itMgr; @Inject protected ConsoleProxyDao _cpDao; - + @Inject ConfigurationDao _configDao; - + public int getVncPort(VMInstanceVO vm) { if (vm.getHostId() == null) { return -1; @@ -112,7 +111,7 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu if (value != null) { _consoleProxyUrlPort = NumbersUtil.parseInt(value, ConsoleProxyManager.DEFAULT_PROXY_URL_PORT); } - + value = configs.get("consoleproxy.port"); if (value != null) { _consoleProxyPort = NumbersUtil.parseInt(value, ConsoleProxyManager.DEFAULT_PROXY_VNC_PORT); @@ -126,10 +125,10 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu _instance = configs.get("instance.name"); _consoleProxyUrlDomain = configs.get("consoleproxy.url.domain"); - + _listener = new ConsoleProxyListener(this); _agentMgr.registerForHostEvents(_listener, true, true, false); - + _itMgr.registerGuru(VirtualMachine.Type.ConsoleProxy, this); if (s_logger.isInfoEnabled()) { @@ -177,20 +176,20 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu } publicIp = host.getPrivateIpAddress(); } - + int urlPort = _consoleProxyUrlPort; if (host.getProxyPort() != null && host.getProxyPort().intValue() > 0) { urlPort = host.getProxyPort().intValue(); } - + return new ConsoleProxyInfo(_sslEnabled, publicIp, _consoleProxyPort, urlPort, _consoleProxyUrlDomain); } else { s_logger.warn("Host that VM is running is no longer available, console access to VM " + userVmId + " will be temporarily unavailable."); } return null; } - + @Override public void onLoadReport(ConsoleProxyLoadReportCommand cmd) { } @@ -273,16 +272,16 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu @Override public void setManagementState(ConsoleProxyManagementState state) { } - + @Override public ConsoleProxyManagementState getManagementState() { - return null; + return null; } - + @Override public void resumeLastManagementState() { } - + @Override public void startAgentHttpHandlerInVM(StartupProxyCommand startupCmd) { } @@ -299,7 +298,7 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu } return VirtualMachineName.getConsoleProxyId(vmName); } - + @Override public ConsoleProxyVO findByName(String name) { // TODO Auto-generated method stub @@ -329,7 +328,7 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu // TODO Auto-generated method stub return false; } - + @Override public boolean finalizeCommandsOnStart(Commands cmds, VirtualMachineProfile profile) { // TODO Auto-generated method stub @@ -346,7 +345,7 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu public void finalizeStop(VirtualMachineProfile profile, StopAnswer answer) { // TODO Auto-generated method stub } - + @Override public void finalizeExpunge(ConsoleProxyVO proxy) { } @@ -366,7 +365,7 @@ public class AgentBasedConsoleProxyManager implements ConsoleProxyManager, Virtu //not supported throw new UnsupportedOperationException("Unplug nic is not supported for vm of type " + vm.getType()); } - + @Override public void prepareStop(VirtualMachineProfile profile) { } diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyBalanceAllocator.java b/server/src/com/cloud/consoleproxy/ConsoleProxyBalanceAllocator.java index 84f6faca7ab..0a045eb9602 100644 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyBalanceAllocator.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyBalanceAllocator.java @@ -27,9 +27,6 @@ import javax.naming.ConfigurationException; import org.springframework.stereotype.Component; -import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.utils.NumbersUtil; -import com.cloud.utils.component.ComponentLocator; import com.cloud.vm.ConsoleProxyVO; import edu.emory.mathcs.backport.java.util.Collections; @@ -37,45 +34,45 @@ import edu.emory.mathcs.backport.java.util.Collections; @Component @Local(value={ConsoleProxyAllocator.class}) public class ConsoleProxyBalanceAllocator implements ConsoleProxyAllocator { - + private String _name; private final Random _rand = new Random(System.currentTimeMillis()); - - @Override - public ConsoleProxyVO allocProxy(List candidates, final Map loadInfo, long dataCenterId) { - if(candidates != null) { - - List allocationList = new ArrayList(); - for(ConsoleProxyVO proxy : candidates) { - allocationList.add(proxy); - } - - Collections.sort(candidates, new Comparator () { - @Override - public int compare(ConsoleProxyVO x, ConsoleProxyVO y) { - Integer loadOfX = loadInfo.get(x.getId()); - Integer loadOfY = loadInfo.get(y.getId()); - if(loadOfX != null && loadOfY != null) { - if(loadOfX < loadOfY) - return -1; - else if(loadOfX > loadOfY) - return 1; - return 0; - } else if(loadOfX == null && loadOfY == null) { - return 0; - } else { - if(loadOfX == null) - return -1; - return 1; - } - } - }); - - if(allocationList.size() > 0) - return allocationList.get(0); - } - return null; + @Override + public ConsoleProxyVO allocProxy(List candidates, final Map loadInfo, long dataCenterId) { + if(candidates != null) { + + List allocationList = new ArrayList(); + for(ConsoleProxyVO proxy : candidates) { + allocationList.add(proxy); + } + + Collections.sort(candidates, new Comparator () { + @Override + public int compare(ConsoleProxyVO x, ConsoleProxyVO y) { + Integer loadOfX = loadInfo.get(x.getId()); + Integer loadOfY = loadInfo.get(y.getId()); + + if(loadOfX != null && loadOfY != null) { + if(loadOfX < loadOfY) + return -1; + else if(loadOfX > loadOfY) + return 1; + return 0; + } else if(loadOfX == null && loadOfY == null) { + return 0; + } else { + if(loadOfX == null) + return -1; + return 1; + } + } + }); + + if(allocationList.size() > 0) + return allocationList.get(0); + } + return null; } @Override @@ -83,7 +80,7 @@ public class ConsoleProxyBalanceAllocator implements ConsoleProxyAllocator { _name = name; return true; } - + @Override public String getName() { return _name; diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index 6ceeb5f5ebe..353ef731ba4 100755 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -19,7 +19,6 @@ package com.cloud.consoleproxy; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Date; -import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -32,6 +31,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import javax.persistence.Table; +import org.apache.cloudstack.api.ServerApiException; import org.apache.log4j.Logger; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; @@ -55,7 +55,6 @@ import com.cloud.agent.api.proxy.StartConsoleProxyAgentHttpHandlerCommand; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.manager.Commands; -import org.apache.cloudstack.api.ServerApiException; import com.cloud.api.commands.DestroyConsoleProxyCmd; import com.cloud.certificate.dao.CertificateDao; import com.cloud.cluster.ClusterManager; @@ -128,8 +127,6 @@ import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; -import com.cloud.utils.component.Adapters; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Manager; import com.cloud.utils.db.DB; import com.cloud.utils.db.GlobalLock; @@ -236,7 +233,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx RulesManager _rulesMgr; @Inject IPAddressDao _ipAddressDao; - + private ConsoleProxyListener _listener; private ServiceOfferingVO _serviceOffering; @@ -269,7 +266,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx private Map _zoneHostInfoMap; // map private Map _zoneProxyCountMap; // map private Map _zoneVmCountMap; // map - + private String _hashKey; private String _staticPublicIp; private int _staticPort; @@ -478,9 +475,9 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx assert (ksVo != null); if (_staticPublicIp == null) { - return new ConsoleProxyInfo(proxy.isSslEnabled(), proxy.getPublicIpAddress(), _consoleProxyPort, proxy.getPort(), ksVo.getDomainSuffix()); + return new ConsoleProxyInfo(proxy.isSslEnabled(), proxy.getPublicIpAddress(), _consoleProxyPort, proxy.getPort(), ksVo.getDomainSuffix()); } else { - return new ConsoleProxyInfo(proxy.isSslEnabled(), _staticPublicIp, _consoleProxyPort, _staticPort, ksVo.getDomainSuffix()); + return new ConsoleProxyInfo(proxy.isSslEnabled(), _staticPublicIp, _consoleProxyPort, _staticPort, ksVo.getDomainSuffix()); } } @@ -809,10 +806,10 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx private ConsoleProxyAllocator getCurrentAllocator() { // for now, only one adapter is supported - for(ConsoleProxyAllocator allocator : _consoleProxyAllocators) { - return allocator; - } - + for(ConsoleProxyAllocator allocator : _consoleProxyAllocators) { + return allocator; + } + return null; } @@ -903,26 +900,26 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx } if(!cmd.isReauthenticating()) { - String ticket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId()); - if (s_logger.isDebugEnabled()) { - s_logger.debug("Console authentication. Ticket in 1 minute boundary for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + ticket); - } - - if (!ticket.equals(ticketInUrl)) { - Date now = new Date(); - // considering of minute round-up - String minuteEarlyTicket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId(), new Date(now.getTime() - 60 * 1000)); - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Console authentication. Ticket in 2-minute boundary for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + minuteEarlyTicket); - } - - if (!minuteEarlyTicket.equals(ticketInUrl)) { - s_logger.error("Access ticket expired or has been modified. vmId: " + cmd.getVmId() + "ticket in URL: " + ticketInUrl + ", tickets to check against: " + ticket + "," - + minuteEarlyTicket); - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - } + String ticket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Console authentication. Ticket in 1 minute boundary for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + ticket); + } + + if (!ticket.equals(ticketInUrl)) { + Date now = new Date(); + // considering of minute round-up + String minuteEarlyTicket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId(), new Date(now.getTime() - 60 * 1000)); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Console authentication. Ticket in 2-minute boundary for " + cmd.getHost() + ":" + cmd.getPort() + "-" + cmd.getVmId() + " is " + minuteEarlyTicket); + } + + if (!minuteEarlyTicket.equals(ticketInUrl)) { + s_logger.error("Access ticket expired or has been modified. vmId: " + cmd.getVmId() + "ticket in URL: " + ticketInUrl + ", tickets to check against: " + ticket + "," + + minuteEarlyTicket); + return new ConsoleAccessAuthenticationAnswer(cmd, false); + } + } } if (cmd.getVmId() != null && cmd.getVmId().isEmpty()) { @@ -959,38 +956,38 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx s_logger.warn("sid " + sid + " in url does not match stored sid " + vm.getVncPassword()); return new ConsoleAccessAuthenticationAnswer(cmd, false); } - + if(cmd.isReauthenticating()) { ConsoleAccessAuthenticationAnswer authenticationAnswer = new ConsoleAccessAuthenticationAnswer(cmd, true); authenticationAnswer.setReauthenticating(true); s_logger.info("Re-authentication request, ask host " + vm.getHostId() + " for new console info"); - GetVncPortAnswer answer = (GetVncPortAnswer) _agentMgr.easySend(vm.getHostId(), new - GetVncPortCommand(vm.getId(), vm.getInstanceName())); + GetVncPortAnswer answer = (GetVncPortAnswer) _agentMgr.easySend(vm.getHostId(), new + GetVncPortCommand(vm.getId(), vm.getInstanceName())); if (answer != null && answer.getResult()) { - Ternary parsedHostInfo = ConsoleProxyServlet.parseHostInfo(answer.getAddress()); - - if(parsedHostInfo.second() != null && parsedHostInfo.third() != null) { - + Ternary parsedHostInfo = ConsoleProxyServlet.parseHostInfo(answer.getAddress()); + + if(parsedHostInfo.second() != null && parsedHostInfo.third() != null) { + s_logger.info("Re-authentication result. vm: " + vm.getId() + ", tunnel url: " + parsedHostInfo.second() - + ", tunnel session: " + parsedHostInfo.third()); - - authenticationAnswer.setTunnelUrl(parsedHostInfo.second()); - authenticationAnswer.setTunnelSession(parsedHostInfo.third()); - } else { + + ", tunnel session: " + parsedHostInfo.third()); + + authenticationAnswer.setTunnelUrl(parsedHostInfo.second()); + authenticationAnswer.setTunnelSession(parsedHostInfo.third()); + } else { s_logger.info("Re-authentication result. vm: " + vm.getId() + ", host address: " + parsedHostInfo.first() - + ", port: " + answer.getPort()); - - authenticationAnswer.setHost(parsedHostInfo.first()); - authenticationAnswer.setPort(answer.getPort()); - } + + ", port: " + answer.getPort()); + + authenticationAnswer.setHost(parsedHostInfo.first()); + authenticationAnswer.setPort(answer.getPort()); + } } else { s_logger.warn("Re-authentication request failed"); - - authenticationAnswer.setSuccess(false); + + authenticationAnswer.setSuccess(false); } - + return authenticationAnswer; } @@ -1198,11 +1195,11 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx } } else { if (s_logger.isDebugEnabled()) { - if (secondaryStorageHost != null) { - s_logger.debug("Zone host is ready, but console proxy template: " + template.getId() + " is not ready on secondary storage: " + secondaryStorageHost.getId()); - } else { - s_logger.debug("Zone host is ready, but console proxy template: " + template.getId() + " is not ready on secondary storage."); - } + if (secondaryStorageHost != null) { + s_logger.debug("Zone host is ready, but console proxy template: " + template.getId() + " is not ready on secondary storage: " + secondaryStorageHost.getId()); + } else { + s_logger.debug("Zone host is ready, but console proxy template: " + template.getId() + " is not ready on secondary storage."); + } } } } @@ -1411,7 +1408,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx result = result && _hostDao.remove(host.getId()); } } - + return result; } catch (ResourceUnavailableException e) { s_logger.warn("Unable to expunge " + proxy, e); @@ -1514,11 +1511,11 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx _itMgr.registerGuru(VirtualMachine.Type.ConsoleProxy, this); boolean useLocalStorage = Boolean.parseBoolean(configs.get(Config.SystemVMUseLocalStorage.key())); - + //check if there is a default service offering configured String cpvmSrvcOffIdStr = configs.get(Config.ConsoleProxyServiceOffering.key()); if (cpvmSrvcOffIdStr != null) { - + Long cpvmSrvcOffId = null; try { cpvmSrvcOffId = _identityDao.getIdentityId(DiskOfferingVO.class.getAnnotation(Table.class).name(),cpvmSrvcOffIdStr); @@ -1532,8 +1529,8 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx } if(_serviceOffering == null || !_serviceOffering.getSystemUse()){ - int ramSize = NumbersUtil.parseInt(_configDao.getValue("console.ram.size"), DEFAULT_PROXY_VM_RAMSIZE); - int cpuFreq = NumbersUtil.parseInt(_configDao.getValue("console.cpu.mhz"), DEFAULT_PROXY_VM_CPUMHZ); + int ramSize = NumbersUtil.parseInt(_configDao.getValue("console.ram.size"), DEFAULT_PROXY_VM_RAMSIZE); + int cpuFreq = NumbersUtil.parseInt(_configDao.getValue("console.cpu.mhz"), DEFAULT_PROXY_VM_CPUMHZ); _serviceOffering = new ServiceOfferingVO("System Offering For Console Proxy", 1, ramSize, cpuFreq, 0, 0, false, null, useLocalStorage, true, null, true, VirtualMachine.Type.ConsoleProxy, true); _serviceOffering.setUniqueName(ServiceOffering.consoleProxyDefaultOffUniqueName); _serviceOffering = _offeringDao.persistSystemServiceOffering(_serviceOffering); @@ -1552,7 +1549,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx _staticPublicIp = _configDao.getValue("consoleproxy.static.publicIp"); if (_staticPublicIp != null) { - _staticPort = NumbersUtil.parseInt(_configDao.getValue("consoleproxy.static.port"), 8443); + _staticPort = NumbersUtil.parseInt(_configDao.getValue("consoleproxy.static.port"), 8443); } if (s_logger.isInfoEnabled()) { @@ -2011,7 +2008,7 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx sc.addAnd(sc.getEntity().getName(), Op.EQ, name); return sc.find(); } - + public String getHashKey() { // although we may have race conditioning here, database transaction serialization should // give us the same key @@ -2036,8 +2033,8 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx //not supported throw new UnsupportedOperationException("Unplug nic is not supported for vm of type " + vm.getType()); } - - @Override - public void prepareStop(VirtualMachineProfile profile) { - } + + @Override + public void prepareStop(VirtualMachineProfile profile) { + } } diff --git a/server/src/com/cloud/consoleproxy/StaticConsoleProxyManager.java b/server/src/com/cloud/consoleproxy/StaticConsoleProxyManager.java index c28a2e498e3..13d3112c827 100755 --- a/server/src/com/cloud/consoleproxy/StaticConsoleProxyManager.java +++ b/server/src/com/cloud/consoleproxy/StaticConsoleProxyManager.java @@ -30,7 +30,6 @@ import com.cloud.host.Host.Type; import com.cloud.host.HostVO; import com.cloud.info.ConsoleProxyInfo; import com.cloud.resource.ResourceManager; -import com.cloud.utils.component.ComponentLocator; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.ConsoleProxyDao; @@ -41,31 +40,31 @@ public class StaticConsoleProxyManager extends AgentBasedConsoleProxyManager imp @Inject ConsoleProxyDao _proxyDao; @Inject ResourceManager _resourceMgr; @Inject ConfigurationDao _configDao; - + @Override protected HostVO findHost(VMInstanceVO vm) { - + List hosts = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByType(Type.ConsoleProxy, vm.getDataCenterIdToDeployIn()); - + return hosts.isEmpty() ? null : hosts.get(0); } - + @Override public ConsoleProxyInfo assignProxy(long dataCenterId, long userVmId) { return new ConsoleProxyInfo(false, _ip, _consoleProxyPort, _consoleProxyUrlPort, _consoleProxyUrlDomain); } - + @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); - + Map dbParams = _configDao.getConfiguration("ManagementServer", params); - + _ip = dbParams.get("public.ip"); if (_ip == null) { _ip = "127.0.0.1"; } - + return true; } } diff --git a/server/src/com/cloud/dc/dao/ClusterDaoImpl.java b/server/src/com/cloud/dc/dao/ClusterDaoImpl.java index f06b24daae5..86dc65e05bd 100644 --- a/server/src/com/cloud/dc/dao/ClusterDaoImpl.java +++ b/server/src/com/cloud/dc/dao/ClusterDaoImpl.java @@ -33,7 +33,6 @@ import com.cloud.dc.ClusterVO; import com.cloud.dc.HostPodVO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.org.Grouping; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; @@ -53,73 +52,73 @@ public class ClusterDaoImpl extends GenericDaoBase implements C protected final SearchBuilder AvailHyperSearch; protected final SearchBuilder ZoneSearch; protected final SearchBuilder ZoneHyTypeSearch; - + private static final String GET_POD_CLUSTER_MAP_PREFIX = "SELECT pod_id, id FROM cloud.cluster WHERE cluster.id IN( "; private static final String GET_POD_CLUSTER_MAP_SUFFIX = " )"; @Inject protected HostPodDao _hostPodDao; - + public ClusterDaoImpl() { super(); - + HyTypeWithoutGuidSearch = createSearchBuilder(); HyTypeWithoutGuidSearch.and("hypervisorType", HyTypeWithoutGuidSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ); HyTypeWithoutGuidSearch.and("guid", HyTypeWithoutGuidSearch.entity().getGuid(), SearchCriteria.Op.NULL); HyTypeWithoutGuidSearch.done(); - + ZoneHyTypeSearch = createSearchBuilder(); ZoneHyTypeSearch.and("hypervisorType", ZoneHyTypeSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ); ZoneHyTypeSearch.and("dataCenterId", ZoneHyTypeSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); ZoneHyTypeSearch.done(); - + PodSearch = createSearchBuilder(); PodSearch.and("pod", PodSearch.entity().getPodId(), SearchCriteria.Op.EQ); PodSearch.and("name", PodSearch.entity().getName(), SearchCriteria.Op.EQ); PodSearch.done(); - + ZoneSearch = createSearchBuilder(); ZoneSearch.and("dataCenterId", ZoneSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); ZoneSearch.groupBy(ZoneSearch.entity().getHypervisorType()); ZoneSearch.done(); - + AvailHyperSearch = createSearchBuilder(); AvailHyperSearch.and("zoneId", AvailHyperSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); AvailHyperSearch.select(null, Func.DISTINCT, AvailHyperSearch.entity().getHypervisorType()); AvailHyperSearch.done(); } - + @Override public List listByZoneId(long zoneId) { SearchCriteria sc = ZoneSearch.create(); sc.setParameters("dataCenterId", zoneId); return listBy(sc); } - + @Override public List listByPodId(long podId) { SearchCriteria sc = PodSearch.create(); sc.setParameters("pod", podId); - + return listBy(sc); } - + @Override public ClusterVO findBy(String name, long podId) { SearchCriteria sc = PodSearch.create(); sc.setParameters("pod", podId); sc.setParameters("name", name); - + return findOneBy(sc); } - + @Override public List listByHyTypeWithoutGuid(String hyType) { SearchCriteria sc = HyTypeWithoutGuidSearch.create(); sc.setParameters("hypervisorType", hyType); - + return listBy(sc); } - + @Override public List listByDcHyType(long dcId, String hyType) { SearchCriteria sc = ZoneHyTypeSearch.create(); @@ -127,7 +126,7 @@ public class ClusterDaoImpl extends GenericDaoBase implements C sc.setParameters("hypervisorType", hyType); return listBy(sc); } - + @Override public List getAvailableHypervisorInZone(Long zoneId) { SearchCriteria sc = AvailHyperSearch.create(); @@ -139,13 +138,13 @@ public class ClusterDaoImpl extends GenericDaoBase implements C for (ClusterVO cluster : clusters) { hypers.add(cluster.getHypervisorType()); } - + return hypers; } - + @Override public Map> getPodClusterIdMap(List clusterIds){ - Transaction txn = Transaction.currentTxn(); + Transaction txn = Transaction.currentTxn(); PreparedStatement pstmt = null; Map> result = new HashMap>(); @@ -158,20 +157,20 @@ public class ClusterDaoImpl extends GenericDaoBase implements C sql.delete(sql.length()-1, sql.length()); sql.append(GET_POD_CLUSTER_MAP_SUFFIX); } - + pstmt = txn.prepareAutoCloseStatement(sql.toString()); ResultSet rs = pstmt.executeQuery(); while (rs.next()) { - Long podId = rs.getLong(1); - Long clusterIdInPod = rs.getLong(2); + Long podId = rs.getLong(1); + Long clusterIdInPod = rs.getLong(2); if(result.containsKey(podId)){ - List clusterList = result.get(podId); - clusterList.add(clusterIdInPod); - result.put(podId, clusterList); + List clusterList = result.get(podId); + clusterList.add(clusterIdInPod); + result.put(podId, clusterList); }else{ - List clusterList = new ArrayList(); - clusterList.add(clusterIdInPod); - result.put(podId, clusterList); + List clusterList = new ArrayList(); + clusterList.add(clusterIdInPod); + result.put(podId, clusterList); } } return result; @@ -181,49 +180,49 @@ public class ClusterDaoImpl extends GenericDaoBase implements C throw new CloudRuntimeException("Caught: " + GET_POD_CLUSTER_MAP_PREFIX, e); } } - + @Override public List listDisabledClusters(long zoneId, Long podId) { - GenericSearchBuilder clusterIdSearch = createSearchBuilder(Long.class); - clusterIdSearch.selectField(clusterIdSearch.entity().getId()); - clusterIdSearch.and("dataCenterId", clusterIdSearch.entity().getDataCenterId(), Op.EQ); - if(podId != null){ - clusterIdSearch.and("podId", clusterIdSearch.entity().getPodId(), Op.EQ); - } - clusterIdSearch.and("allocationState", clusterIdSearch.entity().getAllocationState(), Op.EQ); - clusterIdSearch.done(); + GenericSearchBuilder clusterIdSearch = createSearchBuilder(Long.class); + clusterIdSearch.selectField(clusterIdSearch.entity().getId()); + clusterIdSearch.and("dataCenterId", clusterIdSearch.entity().getDataCenterId(), Op.EQ); + if(podId != null){ + clusterIdSearch.and("podId", clusterIdSearch.entity().getPodId(), Op.EQ); + } + clusterIdSearch.and("allocationState", clusterIdSearch.entity().getAllocationState(), Op.EQ); + clusterIdSearch.done(); - - SearchCriteria sc = clusterIdSearch.create(); + + SearchCriteria sc = clusterIdSearch.create(); sc.addAnd("dataCenterId", SearchCriteria.Op.EQ, zoneId); if (podId != null) { - sc.addAnd("podId", SearchCriteria.Op.EQ, podId); - } + sc.addAnd("podId", SearchCriteria.Op.EQ, podId); + } sc.addAnd("allocationState", SearchCriteria.Op.EQ, Grouping.AllocationState.Disabled); return customSearch(sc, null); } @Override public List listClustersWithDisabledPods(long zoneId) { - - GenericSearchBuilder disabledPodIdSearch = _hostPodDao.createSearchBuilder(Long.class); - disabledPodIdSearch.selectField(disabledPodIdSearch.entity().getId()); - disabledPodIdSearch.and("dataCenterId", disabledPodIdSearch.entity().getDataCenterId(), Op.EQ); - disabledPodIdSearch.and("allocationState", disabledPodIdSearch.entity().getAllocationState(), Op.EQ); - GenericSearchBuilder clusterIdSearch = createSearchBuilder(Long.class); - clusterIdSearch.selectField(clusterIdSearch.entity().getId()); - clusterIdSearch.join("disabledPodIdSearch", disabledPodIdSearch, clusterIdSearch.entity().getPodId(), disabledPodIdSearch.entity().getId(), JoinBuilder.JoinType.INNER); - clusterIdSearch.done(); + GenericSearchBuilder disabledPodIdSearch = _hostPodDao.createSearchBuilder(Long.class); + disabledPodIdSearch.selectField(disabledPodIdSearch.entity().getId()); + disabledPodIdSearch.and("dataCenterId", disabledPodIdSearch.entity().getDataCenterId(), Op.EQ); + disabledPodIdSearch.and("allocationState", disabledPodIdSearch.entity().getAllocationState(), Op.EQ); - - SearchCriteria sc = clusterIdSearch.create(); + GenericSearchBuilder clusterIdSearch = createSearchBuilder(Long.class); + clusterIdSearch.selectField(clusterIdSearch.entity().getId()); + clusterIdSearch.join("disabledPodIdSearch", disabledPodIdSearch, clusterIdSearch.entity().getPodId(), disabledPodIdSearch.entity().getId(), JoinBuilder.JoinType.INNER); + clusterIdSearch.done(); + + + SearchCriteria sc = clusterIdSearch.create(); sc.setJoinParameters("disabledPodIdSearch", "dataCenterId", zoneId); sc.setJoinParameters("disabledPodIdSearch", "allocationState", Grouping.AllocationState.Disabled); - + return customSearch(sc, null); } - + @Override public boolean remove(Long id) { Transaction txn = Transaction.currentTxn(); @@ -231,7 +230,7 @@ public class ClusterDaoImpl extends GenericDaoBase implements C ClusterVO cluster = createForUpdate(); cluster.setName(null); cluster.setGuid(null); - + update(id, cluster); boolean result = super.remove(id); diff --git a/server/src/com/cloud/dc/dao/HostPodDaoImpl.java b/server/src/com/cloud/dc/dao/HostPodDaoImpl.java index a06bd3fe10a..4844d028aee 100644 --- a/server/src/com/cloud/dc/dao/HostPodDaoImpl.java +++ b/server/src/com/cloud/dc/dao/HostPodDaoImpl.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.List; import javax.ejb.Local; +import javax.inject.Inject; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -37,7 +38,6 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.Transaction; -import com.cloud.utils.component.ComponentLocator; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.VMInstanceDaoImpl; @@ -46,32 +46,32 @@ import com.cloud.vm.dao.VMInstanceDaoImpl; @Local(value={HostPodDao.class}) public class HostPodDaoImpl extends GenericDaoBase implements HostPodDao { private static final Logger s_logger = Logger.getLogger(HostPodDaoImpl.class); - - protected SearchBuilder DataCenterAndNameSearch; - protected SearchBuilder DataCenterIdSearch; - - protected HostPodDaoImpl() { - DataCenterAndNameSearch = createSearchBuilder(); - DataCenterAndNameSearch.and("dc", DataCenterAndNameSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); - DataCenterAndNameSearch.and("name", DataCenterAndNameSearch.entity().getName(), SearchCriteria.Op.EQ); - DataCenterAndNameSearch.done(); - - DataCenterIdSearch = createSearchBuilder(); - DataCenterIdSearch.and("dcId", DataCenterIdSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); - DataCenterIdSearch.done(); - } - - @Override + @Inject VMInstanceDaoImpl _vmDao; + + protected SearchBuilder DataCenterAndNameSearch; + protected SearchBuilder DataCenterIdSearch; + + protected HostPodDaoImpl() { + DataCenterAndNameSearch = createSearchBuilder(); + DataCenterAndNameSearch.and("dc", DataCenterAndNameSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + DataCenterAndNameSearch.and("name", DataCenterAndNameSearch.entity().getName(), SearchCriteria.Op.EQ); + DataCenterAndNameSearch.done(); + + DataCenterIdSearch = createSearchBuilder(); + DataCenterIdSearch.and("dcId", DataCenterIdSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + DataCenterIdSearch.done(); + } + + @Override public List listByDataCenterId(long id) { - SearchCriteria sc = DataCenterIdSearch.create(); - sc.setParameters("dcId", id); - - return listBy(sc); - } - - @Override + SearchCriteria sc = DataCenterIdSearch.create(); + sc.setParameters("dcId", id); + + return listBy(sc); + } + + @Override public List listByDataCenterIdVMTypeAndStates(long id, VirtualMachine.Type type, VirtualMachine.State... states) { - final VMInstanceDaoImpl _vmDao = ComponentLocator.inject(VMInstanceDaoImpl.class); SearchBuilder vmInstanceSearch = _vmDao.createSearchBuilder(); vmInstanceSearch.and("type", vmInstanceSearch.entity().getType(), SearchCriteria.Op.EQ); vmInstanceSearch.and("states", vmInstanceSearch.entity().getState(), SearchCriteria.Op.IN); @@ -90,51 +90,51 @@ public class HostPodDaoImpl extends GenericDaoBase implements H return listBy(sc); } - @Override + @Override public HostPodVO findByName(String name, long dcId) { - SearchCriteria sc = DataCenterAndNameSearch.create(); - sc.setParameters("dc", dcId); - sc.setParameters("name", name); - - return findOneBy(sc); - } - - @Override - public HashMap> getCurrentPodCidrSubnets(long zoneId, long podIdToSkip) { - HashMap> currentPodCidrSubnets = new HashMap>(); - - String selectSql = "SELECT id, cidr_address, cidr_size FROM host_pod_ref WHERE data_center_id=" + zoneId +" and removed IS NULL"; - Transaction txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(selectSql); - ResultSet rs = stmt.executeQuery(); - while (rs.next()) { - Long podId = rs.getLong("id"); - if (podId.longValue() == podIdToSkip) { + SearchCriteria sc = DataCenterAndNameSearch.create(); + sc.setParameters("dc", dcId); + sc.setParameters("name", name); + + return findOneBy(sc); + } + + @Override + public HashMap> getCurrentPodCidrSubnets(long zoneId, long podIdToSkip) { + HashMap> currentPodCidrSubnets = new HashMap>(); + + String selectSql = "SELECT id, cidr_address, cidr_size FROM host_pod_ref WHERE data_center_id=" + zoneId +" and removed IS NULL"; + Transaction txn = Transaction.currentTxn(); + try { + PreparedStatement stmt = txn.prepareAutoCloseStatement(selectSql); + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + Long podId = rs.getLong("id"); + if (podId.longValue() == podIdToSkip) { continue; } - String cidrAddress = rs.getString("cidr_address"); - long cidrSize = rs.getLong("cidr_size"); - List cidrPair = new ArrayList(); - cidrPair.add(0, cidrAddress); - cidrPair.add(1, new Long(cidrSize)); - currentPodCidrSubnets.put(podId, cidrPair); - } + String cidrAddress = rs.getString("cidr_address"); + long cidrSize = rs.getLong("cidr_size"); + List cidrPair = new ArrayList(); + cidrPair.add(0, cidrAddress); + cidrPair.add(1, new Long(cidrSize)); + currentPodCidrSubnets.put(podId, cidrPair); + } } catch (SQLException ex) { - s_logger.warn("DB exception " + ex.getMessage(), ex); + s_logger.warn("DB exception " + ex.getMessage(), ex); return null; } - + return currentPodCidrSubnets; - } - + } + @Override public boolean remove(Long id) { Transaction txn = Transaction.currentTxn(); txn.start(); HostPodVO pod = createForUpdate(); pod.setName(null); - + update(id, pod); boolean result = super.remove(id); @@ -150,11 +150,11 @@ public class HostPodDaoImpl extends GenericDaoBase implements H podIdSearch.and("allocationState", podIdSearch.entity().getAllocationState(), Op.EQ); podIdSearch.done(); - + SearchCriteria sc = podIdSearch.create(); sc.addAnd("dataCenterId", SearchCriteria.Op.EQ, zoneId); sc.addAnd("allocationState", SearchCriteria.Op.EQ, Grouping.AllocationState.Disabled); return customSearch(sc, null); } - + } diff --git a/server/src/com/cloud/dc/dao/VlanDaoImpl.java b/server/src/com/cloud/dc/dao/VlanDaoImpl.java index 8fc0580aaef..c5a635fd0c0 100644 --- a/server/src/com/cloud/dc/dao/VlanDaoImpl.java +++ b/server/src/com/cloud/dc/dao/VlanDaoImpl.java @@ -36,7 +36,6 @@ import com.cloud.dc.Vlan.VlanType; import com.cloud.dc.VlanVO; import com.cloud.network.dao.IPAddressDao; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.JoinBuilder; @@ -48,56 +47,56 @@ import com.cloud.utils.exception.CloudRuntimeException; @Component @Local(value={VlanDao.class}) public class VlanDaoImpl extends GenericDaoBase implements VlanDao { - - private final String FindZoneWideVlans = "SELECT * FROM vlan WHERE data_center_id=? and vlan_type=? and vlan_id!=? and id not in (select vlan_db_id from account_vlan_map)"; - - protected SearchBuilder ZoneVlanIdSearch; - protected SearchBuilder ZoneSearch; - protected SearchBuilder ZoneTypeSearch; - protected SearchBuilder ZoneTypeAllPodsSearch; - protected SearchBuilder ZoneTypePodSearch; - protected SearchBuilder ZoneVlanSearch; - protected SearchBuilder NetworkVlanSearch; - protected SearchBuilder PhysicalNetworkVlanSearch; - @Inject protected PodVlanMapDao _podVlanMapDao; - @Inject protected AccountVlanMapDao _accountVlanMapDao; - @Inject protected IPAddressDao _ipAddressDao; - + private final String FindZoneWideVlans = "SELECT * FROM vlan WHERE data_center_id=? and vlan_type=? and vlan_id!=? and id not in (select vlan_db_id from account_vlan_map)"; + + protected SearchBuilder ZoneVlanIdSearch; + protected SearchBuilder ZoneSearch; + protected SearchBuilder ZoneTypeSearch; + protected SearchBuilder ZoneTypeAllPodsSearch; + protected SearchBuilder ZoneTypePodSearch; + protected SearchBuilder ZoneVlanSearch; + protected SearchBuilder NetworkVlanSearch; + protected SearchBuilder PhysicalNetworkVlanSearch; + + @Inject protected PodVlanMapDao _podVlanMapDao; + @Inject protected AccountVlanMapDao _accountVlanMapDao; + @Inject protected IPAddressDao _ipAddressDao; + @Override public VlanVO findByZoneAndVlanId(long zoneId, String vlanId) { - SearchCriteria sc = ZoneVlanIdSearch.create(); - sc.setParameters("zoneId", zoneId); - sc.setParameters("vlanId", vlanId); + SearchCriteria sc = ZoneVlanIdSearch.create(); + sc.setParameters("zoneId", zoneId); + sc.setParameters("vlanId", vlanId); return findOneBy(sc); } - + @Override public List listByZone(long zoneId) { - SearchCriteria sc = ZoneSearch.create(); - sc.setParameters("zoneId", zoneId); - return listBy(sc); + SearchCriteria sc = ZoneSearch.create(); + sc.setParameters("zoneId", zoneId); + return listBy(sc); } - + public VlanDaoImpl() { - ZoneVlanIdSearch = createSearchBuilder(); - ZoneVlanIdSearch.and("zoneId", ZoneVlanIdSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + ZoneVlanIdSearch = createSearchBuilder(); + ZoneVlanIdSearch.and("zoneId", ZoneVlanIdSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); ZoneVlanIdSearch.and("vlanId", ZoneVlanIdSearch.entity().getVlanTag(), SearchCriteria.Op.EQ); ZoneVlanIdSearch.done(); - + ZoneSearch = createSearchBuilder(); ZoneSearch.and("zoneId", ZoneSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); ZoneSearch.done(); - + ZoneTypeSearch = createSearchBuilder(); ZoneTypeSearch.and("zoneId", ZoneTypeSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); ZoneTypeSearch.and("vlanType", ZoneTypeSearch.entity().getVlanType(), SearchCriteria.Op.EQ); ZoneTypeSearch.done(); - + NetworkVlanSearch = createSearchBuilder(); NetworkVlanSearch.and("networkOfferingId", NetworkVlanSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); NetworkVlanSearch.done(); - + PhysicalNetworkVlanSearch = createSearchBuilder(); PhysicalNetworkVlanSearch.and("physicalNetworkId", PhysicalNetworkVlanSearch.entity().getPhysicalNetworkId(), SearchCriteria.Op.EQ); PhysicalNetworkVlanSearch.done(); @@ -105,211 +104,211 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao @Override public List listZoneWideVlans(long zoneId, VlanType vlanType, String vlanId){ - SearchCriteria sc = ZoneVlanSearch.create(); - sc.setParameters("zoneId", zoneId); - sc.setParameters("vlanId", vlanId); - sc.setParameters("vlanType", vlanType); - return listBy(sc); - } - - @Override - public List listByZoneAndType(long zoneId, VlanType vlanType) { - SearchCriteria sc = ZoneTypeSearch.create(); - sc.setParameters("zoneId", zoneId); - sc.setParameters("vlanType", vlanType); + SearchCriteria sc = ZoneVlanSearch.create(); + sc.setParameters("zoneId", zoneId); + sc.setParameters("vlanId", vlanId); + sc.setParameters("vlanType", vlanType); return listBy(sc); - } - - - @Override + } + + @Override + public List listByZoneAndType(long zoneId, VlanType vlanType) { + SearchCriteria sc = ZoneTypeSearch.create(); + sc.setParameters("zoneId", zoneId); + sc.setParameters("vlanType", vlanType); + return listBy(sc); + } + + + @Override public List listByType(VlanType vlanType) { SearchCriteria sc = ZoneTypeSearch.create(); sc.setParameters("vlanType", vlanType); return listBy(sc); } - @Override - public List listVlansForPod(long podId) { - //FIXME: use a join statement to improve the performance (should be minor since we expect only one or two - List vlanMaps = _podVlanMapDao.listPodVlanMapsByPod(podId); - List result = new ArrayList(); - for (PodVlanMapVO pvmvo: vlanMaps) { - result.add(findById(pvmvo.getVlanDbId())); - } - return result; - } + @Override + public List listVlansForPod(long podId) { + //FIXME: use a join statement to improve the performance (should be minor since we expect only one or two + List vlanMaps = _podVlanMapDao.listPodVlanMapsByPod(podId); + List result = new ArrayList(); + for (PodVlanMapVO pvmvo: vlanMaps) { + result.add(findById(pvmvo.getVlanDbId())); + } + return result; + } - @Override - public List listVlansForPodByType(long podId, VlanType vlanType) { - //FIXME: use a join statement to improve the performance (should be minor since we expect only one or two) - List vlanMaps = _podVlanMapDao.listPodVlanMapsByPod(podId); - List result = new ArrayList(); - for (PodVlanMapVO pvmvo: vlanMaps) { - VlanVO vlan =findById(pvmvo.getVlanDbId()); - if (vlan.getVlanType() == vlanType) { - result.add(vlan); - } - } - return result; - } - - @Override - public List listVlansForAccountByType(Long zoneId, long accountId, VlanType vlanType) { - //FIXME: use a join statement to improve the performance (should be minor since we expect only one or two) - List vlanMaps = _accountVlanMapDao.listAccountVlanMapsByAccount(accountId); - List result = new ArrayList(); - for (AccountVlanMapVO acvmvo: vlanMaps) { - VlanVO vlan =findById(acvmvo.getVlanDbId()); - if (vlan.getVlanType() == vlanType && (zoneId == null || vlan.getDataCenterId() == zoneId)) { - result.add(vlan); - } - } - return result; - } + @Override + public List listVlansForPodByType(long podId, VlanType vlanType) { + //FIXME: use a join statement to improve the performance (should be minor since we expect only one or two) + List vlanMaps = _podVlanMapDao.listPodVlanMapsByPod(podId); + List result = new ArrayList(); + for (PodVlanMapVO pvmvo: vlanMaps) { + VlanVO vlan =findById(pvmvo.getVlanDbId()); + if (vlan.getVlanType() == vlanType) { + result.add(vlan); + } + } + return result; + } - @Override - public void addToPod(long podId, long vlanDbId) { - PodVlanMapVO pvmvo = new PodVlanMapVO(podId, vlanDbId); - _podVlanMapDao.persist(pvmvo); - - } + @Override + public List listVlansForAccountByType(Long zoneId, long accountId, VlanType vlanType) { + //FIXME: use a join statement to improve the performance (should be minor since we expect only one or two) + List vlanMaps = _accountVlanMapDao.listAccountVlanMapsByAccount(accountId); + List result = new ArrayList(); + for (AccountVlanMapVO acvmvo: vlanMaps) { + VlanVO vlan =findById(acvmvo.getVlanDbId()); + if (vlan.getVlanType() == vlanType && (zoneId == null || vlan.getDataCenterId() == zoneId)) { + result.add(vlan); + } + } + return result; + } - @Override - public boolean configure(String name, Map params) - throws ConfigurationException { - boolean result = super.configure(name, params); + @Override + public void addToPod(long podId, long vlanDbId) { + PodVlanMapVO pvmvo = new PodVlanMapVO(podId, vlanDbId); + _podVlanMapDao.persist(pvmvo); + + } + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + boolean result = super.configure(name, params); ZoneTypeAllPodsSearch = createSearchBuilder(); ZoneTypeAllPodsSearch.and("zoneId", ZoneTypeAllPodsSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); ZoneTypeAllPodsSearch.and("vlanType", ZoneTypeAllPodsSearch.entity().getVlanType(), SearchCriteria.Op.EQ); - + SearchBuilder PodVlanSearch = _podVlanMapDao.createSearchBuilder(); PodVlanSearch.and("podId", PodVlanSearch.entity().getPodId(), SearchCriteria.Op.NNULL); ZoneTypeAllPodsSearch.join("vlan", PodVlanSearch, PodVlanSearch.entity().getVlanDbId(), ZoneTypeAllPodsSearch.entity().getId(), JoinBuilder.JoinType.INNER); - + ZoneTypeAllPodsSearch.done(); PodVlanSearch.done(); - + ZoneTypePodSearch = createSearchBuilder(); ZoneTypePodSearch.and("zoneId", ZoneTypePodSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); ZoneTypePodSearch.and("vlanType", ZoneTypePodSearch.entity().getVlanType(), SearchCriteria.Op.EQ); - + SearchBuilder PodVlanSearch2 = _podVlanMapDao.createSearchBuilder(); PodVlanSearch2.and("podId", PodVlanSearch2.entity().getPodId(), SearchCriteria.Op.EQ); ZoneTypePodSearch.join("vlan", PodVlanSearch2, PodVlanSearch2.entity().getVlanDbId(), ZoneTypePodSearch.entity().getId(), JoinBuilder.JoinType.INNER); PodVlanSearch2.done(); ZoneTypePodSearch.done(); - return result; - } - - private VlanVO findNextVlan(long zoneId, Vlan.VlanType vlanType) { - List allVlans = listByZoneAndType(zoneId, vlanType); - List emptyVlans = new ArrayList(); - List fullVlans = new ArrayList(); - - // Try to find a VLAN that is partially allocated - for (VlanVO vlan : allVlans) { - long vlanDbId = vlan.getId(); - - int countOfAllocatedIps = _ipAddressDao.countIPs(zoneId, vlanDbId, true); - int countOfAllIps = _ipAddressDao.countIPs(zoneId, vlanDbId, false); - - if ((countOfAllocatedIps > 0) && (countOfAllocatedIps < countOfAllIps)) { - return vlan; - } else if (countOfAllocatedIps == 0) { - emptyVlans.add(vlan); - } else if (countOfAllocatedIps == countOfAllIps) { - fullVlans.add(vlan); - } - } - - if (emptyVlans.isEmpty()) { - return null; - } - - // Try to find an empty VLAN with the same tag/subnet as a VLAN that is full - for (VlanVO fullVlan : fullVlans) { - for (VlanVO emptyVlan : emptyVlans) { - if (fullVlan.getVlanTag().equals(emptyVlan.getVlanTag()) && - fullVlan.getVlanGateway().equals(emptyVlan.getVlanGateway()) && - fullVlan.getVlanNetmask().equals(emptyVlan.getVlanNetmask())) { - return emptyVlan; - } - } - } - - // Return a random empty VLAN - return emptyVlans.get(0); - } + return result; + } + + private VlanVO findNextVlan(long zoneId, Vlan.VlanType vlanType) { + List allVlans = listByZoneAndType(zoneId, vlanType); + List emptyVlans = new ArrayList(); + List fullVlans = new ArrayList(); + + // Try to find a VLAN that is partially allocated + for (VlanVO vlan : allVlans) { + long vlanDbId = vlan.getId(); + + int countOfAllocatedIps = _ipAddressDao.countIPs(zoneId, vlanDbId, true); + int countOfAllIps = _ipAddressDao.countIPs(zoneId, vlanDbId, false); + + if ((countOfAllocatedIps > 0) && (countOfAllocatedIps < countOfAllIps)) { + return vlan; + } else if (countOfAllocatedIps == 0) { + emptyVlans.add(vlan); + } else if (countOfAllocatedIps == countOfAllIps) { + fullVlans.add(vlan); + } + } + + if (emptyVlans.isEmpty()) { + return null; + } + + // Try to find an empty VLAN with the same tag/subnet as a VLAN that is full + for (VlanVO fullVlan : fullVlans) { + for (VlanVO emptyVlan : emptyVlans) { + if (fullVlan.getVlanTag().equals(emptyVlan.getVlanTag()) && + fullVlan.getVlanGateway().equals(emptyVlan.getVlanGateway()) && + fullVlan.getVlanNetmask().equals(emptyVlan.getVlanNetmask())) { + return emptyVlan; + } + } + } + + // Return a random empty VLAN + return emptyVlans.get(0); + } + + @Override + public boolean zoneHasDirectAttachUntaggedVlans(long zoneId) { + SearchCriteria sc = ZoneTypeAllPodsSearch.create(); + sc.setParameters("zoneId", zoneId); + sc.setParameters("vlanType", VlanType.DirectAttached); - @Override - public boolean zoneHasDirectAttachUntaggedVlans(long zoneId) { - SearchCriteria sc = ZoneTypeAllPodsSearch.create(); - sc.setParameters("zoneId", zoneId); - sc.setParameters("vlanType", VlanType.DirectAttached); - return listIncludingRemovedBy(sc).size() > 0; - } + } - public Pair assignPodDirectAttachIpAddress(long zoneId, - long podId, long accountId, long domainId) { - SearchCriteria sc = ZoneTypePodSearch.create(); - sc.setParameters("zoneId", zoneId); - sc.setParameters("vlanType", VlanType.DirectAttached); - sc.setJoinParameters("vlan", "podId", podId); - - VlanVO vlan = findOneIncludingRemovedBy(sc); - if (vlan == null) { - return null; - } - - return null; + public Pair assignPodDirectAttachIpAddress(long zoneId, + long podId, long accountId, long domainId) { + SearchCriteria sc = ZoneTypePodSearch.create(); + sc.setParameters("zoneId", zoneId); + sc.setParameters("vlanType", VlanType.DirectAttached); + sc.setJoinParameters("vlan", "podId", podId); + + VlanVO vlan = findOneIncludingRemovedBy(sc); + if (vlan == null) { + return null; + } + + return null; // String ipAddress = _ipAddressDao.assignIpAddress(accountId, domainId, vlan.getId(), false).getAddress(); // if (ipAddress == null) { // return null; // } // return new Pair(ipAddress, vlan); - } - - @Override - @DB - public List searchForZoneWideVlans(long dcId, String vlanType, String vlanId){ - - StringBuilder sql = new StringBuilder(FindZoneWideVlans); + } - Transaction txn = Transaction.currentTxn(); - PreparedStatement pstmt = null; - try { - pstmt = txn.prepareAutoCloseStatement(sql.toString()); - pstmt.setLong(1, dcId); - pstmt.setString(2, vlanType); - pstmt.setString(3, vlanId); - - ResultSet rs = pstmt.executeQuery(); - List zoneWideVlans = new ArrayList(); + @Override + @DB + public List searchForZoneWideVlans(long dcId, String vlanType, String vlanId){ - while (rs.next()) { - zoneWideVlans.add(toEntityBean(rs, false)); - } - - return zoneWideVlans; - } catch (SQLException e) { - throw new CloudRuntimeException("Unable to execute " + pstmt.toString(), e); - } - } - - @Override + StringBuilder sql = new StringBuilder(FindZoneWideVlans); + + Transaction txn = Transaction.currentTxn(); + PreparedStatement pstmt = null; + try { + pstmt = txn.prepareAutoCloseStatement(sql.toString()); + pstmt.setLong(1, dcId); + pstmt.setString(2, vlanType); + pstmt.setString(3, vlanId); + + ResultSet rs = pstmt.executeQuery(); + List zoneWideVlans = new ArrayList(); + + while (rs.next()) { + zoneWideVlans.add(toEntityBean(rs, false)); + } + + return zoneWideVlans; + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to execute " + pstmt.toString(), e); + } + } + + @Override public List listVlansByNetworkId(long networkOfferingId) { - SearchCriteria sc = NetworkVlanSearch.create(); + SearchCriteria sc = NetworkVlanSearch.create(); sc.setParameters("networkOfferingId", networkOfferingId); return listBy(sc); } @Override public List listVlansByPhysicalNetworkId(long physicalNetworkId) { - SearchCriteria sc = PhysicalNetworkVlanSearch.create(); + SearchCriteria sc = PhysicalNetworkVlanSearch.create(); sc.setParameters("physicalNetworkId", physicalNetworkId); return listBy(sc); } diff --git a/server/src/com/cloud/deploy/FirstFitPlanner.java b/server/src/com/cloud/deploy/FirstFitPlanner.java index bcc1d264219..3517bf8ed14 100755 --- a/server/src/com/cloud/deploy/FirstFitPlanner.java +++ b/server/src/com/cloud/deploy/FirstFitPlanner.java @@ -18,7 +18,6 @@ package com.cloud.deploy; import java.util.ArrayList; import java.util.Comparator; -import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -74,7 +73,6 @@ import com.cloud.storage.dao.VolumeDao; import com.cloud.user.AccountManager; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; -import com.cloud.utils.component.Adapters; import com.cloud.vm.DiskProfile; import com.cloud.vm.ReservationContext; import com.cloud.vm.VirtualMachine; @@ -106,7 +104,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { //@com.cloud.utils.component.Inject(adapter=StoragePoolAllocator.class) @Inject protected List _storagePoolAllocators; - + //@com.cloud.utils.component.Inject(adapter=HostAllocator.class) @Inject protected List _hostAllocators; protected String _allocationAlgorithm = "random"; @@ -115,7 +113,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { @Override public DeployDestination plan(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid) - throws InsufficientServerCapacityException { + throws InsufficientServerCapacityException { VirtualMachine vm = vmProfile.getVirtualMachine(); DataCenter dc = _dcDao.findById(vm.getDataCenterIdToDeployIn()); @@ -126,7 +124,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } return null; } - + ServiceOffering offering = vmProfile.getServiceOffering(); int cpu_requested = offering.getCpu() * offering.getSpeed(); long ram_requested = offering.getRamSize() * 1024L * 1024L; @@ -143,9 +141,9 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { s_logger.debug("Is ROOT volume READY (pool already allocated)?: " + (plan.getPoolId()!=null ? "Yes": "No")); } - + String haVmTag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.HaTag); - + if(plan.getHostId() != null && haVmTag == null){ Long hostIdSpecified = plan.getHostId(); if (s_logger.isDebugEnabled()){ @@ -238,7 +236,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } s_logger.debug("Cannot choose the last host to deploy this VM "); } - + List clusterList = new ArrayList(); if (plan.getClusterId() != null) { @@ -272,7 +270,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } } else { s_logger.debug("Searching all possible resources under this Zone: "+ plan.getDataCenterId()); - + boolean applyAllocationAtPods = Boolean.parseBoolean(_configDao.getValue(Config.ApplyAllocationAlgorithmToPods.key())); if(applyAllocationAtPods){ //start scan at all pods under this zone. @@ -284,15 +282,15 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } } - + private DeployDestination scanPodsForDestination(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid){ - + ServiceOffering offering = vmProfile.getServiceOffering(); int requiredCpu = offering.getCpu() * offering.getSpeed(); long requiredRam = offering.getRamSize() * 1024L * 1024L; String opFactor = _configDao.getValue(Config.CPUOverprovisioningFactor.key()); float cpuOverprovisioningFactor = NumbersUtil.parseFloat(opFactor, 1); - + //list pods under this zone by cpu and ram capacity List prioritizedPodIds = new ArrayList(); Pair, Map> podCapacityInfo = listPodsByCapacity(plan.getDataCenterId(), requiredCpu, requiredRam, cpuOverprovisioningFactor); @@ -313,16 +311,16 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } podsWithCapacity.removeAll(disabledPods); } - } + } }else{ if (s_logger.isDebugEnabled()) { s_logger.debug("No pods found having a host with enough capacity, returning."); } return null; } - + if(!podsWithCapacity.isEmpty()){ - + prioritizedPodIds = reorderPods(podCapacityInfo, vmProfile, plan); //loop over pods @@ -345,9 +343,9 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { return null; } } - + private DeployDestination scanClustersForDestinationInZoneOrPod(long id, boolean isZone, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid){ - + VirtualMachine vm = vmProfile.getVirtualMachine(); ServiceOffering offering = vmProfile.getServiceOffering(); DataCenter dc = _dcDao.findById(vm.getDataCenterIdToDeployIn()); @@ -355,7 +353,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { long requiredRam = offering.getRamSize() * 1024L * 1024L; String opFactor = _configDao.getValue(Config.CPUOverprovisioningFactor.key()); float cpuOverprovisioningFactor = NumbersUtil.parseFloat(opFactor, 1); - + //list clusters under this zone by cpu and ram capacity Pair, Map> clusterCapacityInfo = listClustersByCapacity(id, requiredCpu, requiredRam, avoid, isZone, cpuOverprovisioningFactor); List prioritizedClusterIds = clusterCapacityInfo.first(); @@ -366,7 +364,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } prioritizedClusterIds.removeAll(avoid.getClustersToAvoid()); } - + if(!isRootAdmin(plan.getReservationContext())){ List disabledClusters = new ArrayList(); if(isZone){ @@ -397,7 +395,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { return null; } } - + /** * This method should reorder the given list of Cluster Ids by applying any necessary heuristic * for this planner @@ -409,7 +407,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { List reordersClusterIds = clusterCapacityInfo.first(); return reordersClusterIds; } - + /** * This method should reorder the given list of Pod Ids by applying any necessary heuristic * for this planner @@ -421,7 +419,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { List podIdsByCapacity = podCapacityInfo.first(); return podIdsByCapacity; } - + private List listDisabledClusters(long zoneId, Long podId){ List disabledClusters = _clusterDao.listDisabledClusters(zoneId, podId); if(podId == null){ @@ -431,70 +429,70 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } return disabledClusters; } - + private List listDisabledPods(long zoneId){ List disabledPods = _podDao.listDisabledPods(zoneId); return disabledPods; } - + private Map getCapacityThresholdMap(){ - // Lets build this real time so that the admin wont have to restart MS if he changes these values - Map disableThresholdMap = new HashMap(); - - String cpuDisableThresholdString = _configDao.getValue(Config.CPUCapacityDisableThreshold.key()); + // Lets build this real time so that the admin wont have to restart MS if he changes these values + Map disableThresholdMap = new HashMap(); + + String cpuDisableThresholdString = _configDao.getValue(Config.CPUCapacityDisableThreshold.key()); float cpuDisableThreshold = NumbersUtil.parseFloat(cpuDisableThresholdString, 0.85F); disableThresholdMap.put(Capacity.CAPACITY_TYPE_CPU, cpuDisableThreshold); - + String memoryDisableThresholdString = _configDao.getValue(Config.MemoryCapacityDisableThreshold.key()); float memoryDisableThreshold = NumbersUtil.parseFloat(memoryDisableThresholdString, 0.85F); disableThresholdMap.put(Capacity.CAPACITY_TYPE_MEMORY, memoryDisableThreshold); - - return disableThresholdMap; + + return disableThresholdMap; } private List getCapacitiesForCheckingThreshold(){ - List capacityList = new ArrayList(); - capacityList.add(Capacity.CAPACITY_TYPE_CPU); - capacityList.add(Capacity.CAPACITY_TYPE_MEMORY); - return capacityList; + List capacityList = new ArrayList(); + capacityList.add(Capacity.CAPACITY_TYPE_CPU); + capacityList.add(Capacity.CAPACITY_TYPE_MEMORY); + return capacityList; } - + private void removeClustersCrossingThreshold(List clusterListForVmAllocation, ExcludeList avoid, VirtualMachineProfile vmProfile, DeploymentPlan plan){ - - Map capacityThresholdMap = getCapacityThresholdMap(); - List capacityList = getCapacitiesForCheckingThreshold(); - List clustersCrossingThreshold = new ArrayList(); - + + Map capacityThresholdMap = getCapacityThresholdMap(); + List capacityList = getCapacitiesForCheckingThreshold(); + List clustersCrossingThreshold = new ArrayList(); + ServiceOffering offering = vmProfile.getServiceOffering(); int cpu_requested = offering.getCpu() * offering.getSpeed(); long ram_requested = offering.getRamSize() * 1024L * 1024L; - + // For each capacity get the cluster list crossing the threshold and remove it from the clusterList that will be used for vm allocation. for(short capacity : capacityList){ - - if (clusterListForVmAllocation == null || clusterListForVmAllocation.size() == 0){ - return; - } - - if (capacity == Capacity.CAPACITY_TYPE_CPU){ - clustersCrossingThreshold = _capacityDao.listClustersCrossingThreshold(Capacity.CAPACITY_TYPE_CPU, plan.getDataCenterId(), - capacityThresholdMap.get(capacity), cpu_requested, ApiDBUtils.getCpuOverprovisioningFactor()); - }else{ - clustersCrossingThreshold = _capacityDao.listClustersCrossingThreshold(capacity, plan.getDataCenterId(), - capacityThresholdMap.get(capacity), ram_requested, 1.0f);//Mem overprov not supported yet - } - - if (clustersCrossingThreshold != null && clustersCrossingThreshold.size() != 0){ - // addToAvoid Set - avoid.addClusterList(clustersCrossingThreshold); - // Remove clusters crossing disabled threshold - clusterListForVmAllocation.removeAll(clustersCrossingThreshold); - - s_logger.debug("Cannot allocate cluster list " + clustersCrossingThreshold.toString() + " for vm creation since their allocated percentage" + - " crosses the disable capacity threshold: " + capacityThresholdMap.get(capacity) + " for capacity Type : " + capacity + ", skipping these clusters"); - } - + if (clusterListForVmAllocation == null || clusterListForVmAllocation.size() == 0){ + return; + } + + if (capacity == Capacity.CAPACITY_TYPE_CPU){ + clustersCrossingThreshold = _capacityDao.listClustersCrossingThreshold(Capacity.CAPACITY_TYPE_CPU, plan.getDataCenterId(), + capacityThresholdMap.get(capacity), cpu_requested, ApiDBUtils.getCpuOverprovisioningFactor()); + }else{ + clustersCrossingThreshold = _capacityDao.listClustersCrossingThreshold(capacity, plan.getDataCenterId(), + capacityThresholdMap.get(capacity), ram_requested, 1.0f);//Mem overprov not supported yet + } + + + if (clustersCrossingThreshold != null && clustersCrossingThreshold.size() != 0){ + // addToAvoid Set + avoid.addClusterList(clustersCrossingThreshold); + // Remove clusters crossing disabled threshold + clusterListForVmAllocation.removeAll(clustersCrossingThreshold); + + s_logger.debug("Cannot allocate cluster list " + clustersCrossingThreshold.toString() + " for vm creation since their allocated percentage" + + " crosses the disable capacity threshold: " + capacityThresholdMap.get(capacity) + " for capacity Type : " + capacity + ", skipping these clusters"); + } + } } @@ -506,7 +504,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } removeClustersCrossingThreshold(clusterList, avoid, vmProfile, plan); - + for(Long clusterId : clusterList){ Cluster clusterVO = _clusterDao.findById(clusterId); @@ -515,7 +513,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { avoid.addCluster(clusterVO.getId()); continue; } - + s_logger.debug("Checking resources in Cluster: "+clusterId + " under Pod: "+clusterVO.getPodId()); //search for resources(hosts and storage) under this zone, pod, cluster. DataCenterDeployment potentialPlan = new DataCenterDeployment(plan.getDataCenterId(), clusterVO.getPodId(), clusterVO.getId(), null, plan.getPoolId(), null, plan.getReservationContext()); @@ -595,11 +593,11 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { if (s_logger.isTraceEnabled()) { s_logger.trace("ClusterId List having enough CPU and RAM capacity & in order of aggregate capacity: " + clusterIdsOrderedByAggregateCapacity); } - + return result; } - + protected Pair, Map> listPodsByCapacity(long zoneId, int requiredCpu, long requiredRam, float cpuOverprovisioningFactor){ //look at the aggregate available cpu and ram per pod //although an aggregate value may be false indicator that a pod can host a vm, it will at the least eliminate those pods which definitely cannot @@ -632,7 +630,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { if (s_logger.isTraceEnabled()) { s_logger.trace("PodId List having enough CPU and RAM capacity & in order of aggregate capacity: " + podIdsOrderedByAggregateCapacity); } - + return result; } @@ -719,7 +717,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { break; } } - + if(suitableHosts.isEmpty()){ s_logger.debug("No suitable hosts found"); } @@ -801,8 +799,8 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { // when deploying VM based on ISO, we have a service offering and an additional disk offering, use-local storage flag is actually // saved in service offering, overrde the flag from service offering when it is a ROOT disk if(!useLocalStorage && vmProfile.getServiceOffering().getUseLocalStorage()) { - if(toBeCreated.getVolumeType() == Volume.Type.ROOT) - useLocalStorage = true; + if(toBeCreated.getVolumeType() == Volume.Type.ROOT) + useLocalStorage = true; } } diskProfile.setUseLocalStorage(useLocalStorage); @@ -816,7 +814,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { break; } } - + if(!foundPotentialPools){ s_logger.debug("No suitable pools found for volume: "+toBeCreated +" under cluster: "+plan.getClusterId()); //No suitable storage pools found under this cluster for this volume. - remove any suitable pools found for other volumes. @@ -862,7 +860,7 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentPlanner { } return false; } - + @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); diff --git a/server/src/com/cloud/ha/HighAvailabilityManagerImpl.java b/server/src/com/cloud/ha/HighAvailabilityManagerImpl.java index 6df08229830..813728f78ca 100755 --- a/server/src/com/cloud/ha/HighAvailabilityManagerImpl.java +++ b/server/src/com/cloud/ha/HighAvailabilityManagerImpl.java @@ -37,7 +37,6 @@ import com.cloud.agent.AgentManager; import com.cloud.alert.AlertManager; import com.cloud.cluster.ClusterManagerListener; import com.cloud.cluster.ManagementServerHostVO; -import com.cloud.cluster.StackMaid; import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.ClusterDetailsDao; @@ -142,7 +141,7 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu ManagementServer _msServer; @Inject ConfigurationDao _configDao; - + String _instance; ScheduledExecutorService _executor; int _stopRetryInterval; @@ -189,12 +188,12 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu if (host.getType() != Host.Type.Routing) { return; } - + if(host.getHypervisorType() == HypervisorType.VMware) { s_logger.info("Don't restart for VMs on host " + host.getId() + " as the host is VMware host"); - return; + return; } - + s_logger.warn("Scheduling restart for VMs on host " + host.getId()); final List vms = _instanceDao.listByHostId(host.getId()); @@ -268,29 +267,29 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu @Override public void scheduleRestart(VMInstanceVO vm, boolean investigate) { - Long hostId = vm.getHostId(); - if (hostId == null) { - try { - s_logger.debug("Found a vm that is scheduled to be restarted but has no host id: " + vm); + Long hostId = vm.getHostId(); + if (hostId == null) { + try { + s_logger.debug("Found a vm that is scheduled to be restarted but has no host id: " + vm); _itMgr.advanceStop(vm, true, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); } catch (ResourceUnavailableException e) { assert false : "How do we hit this when force is true?"; - throw new CloudRuntimeException("Caught exception even though it should be handled.", e); + throw new CloudRuntimeException("Caught exception even though it should be handled.", e); } catch (OperationTimedoutException e) { assert false : "How do we hit this when force is true?"; - throw new CloudRuntimeException("Caught exception even though it should be handled.", e); + throw new CloudRuntimeException("Caught exception even though it should be handled.", e); } catch (ConcurrentOperationException e) { assert false : "How do we hit this when force is true?"; - throw new CloudRuntimeException("Caught exception even though it should be handled.", e); + throw new CloudRuntimeException("Caught exception even though it should be handled.", e); } - return; - } + return; + } + + if(vm.getHypervisorType() == HypervisorType.VMware) { + s_logger.info("Skip HA for VMware VM " + vm.getInstanceName()); + return; + } - if(vm.getHypervisorType() == HypervisorType.VMware) { - s_logger.info("Skip HA for VMware VM " + vm.getInstanceName()); - return; - } - if (!investigate) { if (s_logger.isDebugEnabled()) { s_logger.debug("VM does not require investigation so I'm marking it as Stopped: " + vm.toString()); @@ -319,13 +318,13 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu _itMgr.advanceStop(vm, true, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); } catch (ResourceUnavailableException e) { assert false : "How do we hit this when force is true?"; - throw new CloudRuntimeException("Caught exception even though it should be handled.", e); + throw new CloudRuntimeException("Caught exception even though it should be handled.", e); } catch (OperationTimedoutException e) { assert false : "How do we hit this when force is true?"; - throw new CloudRuntimeException("Caught exception even though it should be handled.", e); + throw new CloudRuntimeException("Caught exception even though it should be handled.", e); } catch (ConcurrentOperationException e) { assert false : "How do we hit this when force is true?"; - throw new CloudRuntimeException("Caught exception even though it should be handled.", e); + throw new CloudRuntimeException("Caught exception even though it should be handled.", e); } } @@ -360,7 +359,7 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu s_logger.info(str.toString()); return null; } - + items = _haDao.listRunningHaWorkForVm(work.getInstanceId()); if (items.size() > 0) { StringBuilder str = new StringBuilder("Waiting because there's HA work being executed on an item currently. Work Ids =["); @@ -371,7 +370,7 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu s_logger.info(str.toString()); return (System.currentTimeMillis() >> 10) + _investigateRetryInterval; } - + long vmId = work.getInstanceId(); VMInstanceVO vm = _itMgr.findByIdAndType(work.getType(), work.getInstanceId()); @@ -420,14 +419,14 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu Investigator investigator = null; for(Investigator it : _investigators) { - investigator = it; + investigator = it; alive = investigator.isVmAlive(vm, host); s_logger.info(investigator.getName() + " found " + vm + "to be alive? " + alive); if (alive != null) { break; } } - + boolean fenced = false; if (alive == null) { s_logger.debug("Fencing off VM that we don't know the state of"); @@ -439,7 +438,7 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu break; } } - + } else if (!alive) { fenced = true; } else { @@ -464,13 +463,13 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu _itMgr.advanceStop(vm, true, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); } catch (ResourceUnavailableException e) { assert false : "How do we hit this when force is true?"; - throw new CloudRuntimeException("Caught exception even though it should be handled.", e); + throw new CloudRuntimeException("Caught exception even though it should be handled.", e); } catch (OperationTimedoutException e) { assert false : "How do we hit this when force is true?"; - throw new CloudRuntimeException("Caught exception even though it should be handled.", e); + throw new CloudRuntimeException("Caught exception even though it should be handled.", e); } catch (ConcurrentOperationException e) { assert false : "How do we hit this when force is true?"; - throw new CloudRuntimeException("Caught exception even though it should be handled.", e); + throw new CloudRuntimeException("Caught exception even though it should be handled.", e); } work.setStep(Step.Scheduled); @@ -481,13 +480,13 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu _itMgr.advanceStop(vm, true, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); } catch (ResourceUnavailableException e) { assert false : "How do we hit this when force is true?"; - throw new CloudRuntimeException("Caught exception even though it should be handled.", e); + throw new CloudRuntimeException("Caught exception even though it should be handled.", e); } catch (OperationTimedoutException e) { assert false : "How do we hit this when force is true?"; - throw new CloudRuntimeException("Caught exception even though it should be handled.", e); + throw new CloudRuntimeException("Caught exception even though it should be handled.", e); } catch (ConcurrentOperationException e) { assert false : "How do we hit this when force is true?"; - throw new CloudRuntimeException("Caught exception even though it should be handled.", e); + throw new CloudRuntimeException("Caught exception even though it should be handled.", e); } } } @@ -519,7 +518,7 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu params.put(VirtualMachineProfile.Param.HaTag, _haTag); } VMInstanceVO started = _itMgr.advanceStart(vm, params, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); - + if (started != null) { s_logger.info("VM is now restarted: " + vmId + " on " + started.getHostId()); return null; @@ -735,7 +734,7 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu if (_instance == null) { _instance = "VMOPS"; } - + _haTag = params.get("ha.tag"); _haDao.releaseWorkItems(_serverId); @@ -785,8 +784,6 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu _haDao.cleanup(System.currentTimeMillis() - _timeBetweenFailures); } catch (Exception e) { s_logger.warn("Error while cleaning up", e); - } finally { - StackMaid.current().exitCleanup(); } } } @@ -832,7 +829,7 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu nextTime = destroyVM(work); } else { assert false : "How did we get here with " + wt.toString(); - continue; + continue; } if (nextTime == null) { @@ -852,7 +849,6 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu } catch (final Throwable th) { s_logger.error("Caught this throwable, ", th); } finally { - StackMaid.current().exitCleanup(); if (work != null) { NDC.pop(); } @@ -885,5 +881,5 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager, Clu public String getHaTag() { return _haTag; } - + } diff --git a/server/src/com/cloud/host/dao/HostDaoImpl.java b/server/src/com/cloud/host/dao/HostDaoImpl.java index 0767befcb70..2a7139cba54 100755 --- a/server/src/com/cloud/host/dao/HostDaoImpl.java +++ b/server/src/com/cloud/host/dao/HostDaoImpl.java @@ -47,7 +47,6 @@ import com.cloud.info.RunningHostCountInfo; import com.cloud.org.Managed; import com.cloud.resource.ResourceState; import com.cloud.utils.DateUtil; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.Attribute; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; @@ -103,7 +102,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao protected SearchBuilder ManagedDirectConnectSearch; protected SearchBuilder ManagedRoutingServersSearch; protected SearchBuilder SecondaryStorageVMSearch; - + protected GenericSearchBuilder HostsInStatusSearch; protected GenericSearchBuilder CountRoutingByDc; @@ -123,7 +122,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao public HostDaoImpl() { } - + @PostConstruct public void init() { @@ -152,7 +151,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao TypeDcSearch.and("type", TypeDcSearch.entity().getType(), SearchCriteria.Op.EQ); TypeDcSearch.and("dc", TypeDcSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); TypeDcSearch.done(); - + SecondaryStorageVMSearch = createSearchBuilder(); SecondaryStorageVMSearch.and("type", SecondaryStorageVMSearch.entity().getType(), SearchCriteria.Op.EQ); SecondaryStorageVMSearch.and("dc", SecondaryStorageVMSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); @@ -165,14 +164,14 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao TypeDcStatusSearch.and("status", TypeDcStatusSearch.entity().getStatus(), SearchCriteria.Op.EQ); TypeDcStatusSearch.and("resourceState", TypeDcStatusSearch.entity().getResourceState(), SearchCriteria.Op.EQ); TypeDcStatusSearch.done(); - + TypeClusterStatusSearch = createSearchBuilder(); TypeClusterStatusSearch.and("type", TypeClusterStatusSearch.entity().getType(), SearchCriteria.Op.EQ); TypeClusterStatusSearch.and("cluster", TypeClusterStatusSearch.entity().getClusterId(), SearchCriteria.Op.EQ); TypeClusterStatusSearch.and("status", TypeClusterStatusSearch.entity().getStatus(), SearchCriteria.Op.EQ); TypeClusterStatusSearch.and("resourceState", TypeClusterStatusSearch.entity().getResourceState(), SearchCriteria.Op.EQ); TypeClusterStatusSearch.done(); - + IdStatusSearch = createSearchBuilder(); IdStatusSearch.and("id", IdStatusSearch.entity().getId(), SearchCriteria.Op.EQ); IdStatusSearch.and("states", IdStatusSearch.entity().getStatus(), SearchCriteria.Op.IN); @@ -218,7 +217,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao StatusSearch = createSearchBuilder(); StatusSearch.and("status", StatusSearch.entity().getStatus(), SearchCriteria.Op.IN); StatusSearch.done(); - + ResourceStateSearch = createSearchBuilder(); ResourceStateSearch.and("resourceState", ResourceStateSearch.entity().getResourceState(), SearchCriteria.Op.IN); ResourceStateSearch.done(); @@ -261,7 +260,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao ClusterManagedSearch.and("managed", ClusterManagedSearch.entity().getManagedState(), SearchCriteria.Op.EQ); UnmanagedDirectConnectSearch.join("ClusterManagedSearch", ClusterManagedSearch, ClusterManagedSearch.entity().getId(), UnmanagedDirectConnectSearch.entity().getClusterId(), JoinType.INNER); UnmanagedDirectConnectSearch.done(); - + DirectConnectSearch = createSearchBuilder(); DirectConnectSearch.and("resource", DirectConnectSearch.entity().getResource(), SearchCriteria.Op.NNULL); @@ -311,7 +310,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao ManagedRoutingServersSearch.and("server", ManagedRoutingServersSearch.entity().getManagementServerId(), SearchCriteria.Op.NNULL); ManagedRoutingServersSearch.and("type", ManagedRoutingServersSearch.entity().getType(), SearchCriteria.Op.EQ); ManagedRoutingServersSearch.done(); - + RoutingSearch = createSearchBuilder(); RoutingSearch.and("type", RoutingSearch.entity().getType(), SearchCriteria.Op.EQ); RoutingSearch.done(); @@ -334,52 +333,52 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao List hosts = listBy(sc); return hosts.size(); } - + @Override public HostVO findByGuid(String guid) { SearchCriteria sc = GuidSearch.create("guid", guid); return findOneBy(sc); } - + @Override @DB public List findAndUpdateDirectAgentToLoad(long lastPingSecondsAfter, Long limit, long managementServerId) { Transaction txn = Transaction.currentTxn(); txn.start(); - SearchCriteria sc = UnmanagedDirectConnectSearch.create(); - sc.setParameters("lastPinged", lastPingSecondsAfter); + SearchCriteria sc = UnmanagedDirectConnectSearch.create(); + sc.setParameters("lastPinged", lastPingSecondsAfter); //sc.setParameters("resourceStates", ResourceState.ErrorInMaintenance, ResourceState.Maintenance, ResourceState.PrepareForMaintenance, ResourceState.Disabled); sc.setJoinParameters("ClusterManagedSearch", "managed", Managed.ManagedState.Managed); List hosts = lockRows(sc, new Filter(HostVO.class, "clusterId", true, 0L, limit), true); - + for (HostVO host : hosts) { host.setManagementServerId(managementServerId); update(host.getId(), host); } - + txn.commit(); - + return hosts; } - + @Override @DB public List findAndUpdateApplianceToLoad(long lastPingSecondsAfter, long managementServerId) { - Transaction txn = Transaction.currentTxn(); - - txn.start(); - SearchCriteria sc = UnmanagedApplianceSearch.create(); - sc.setParameters("lastPinged", lastPingSecondsAfter); + Transaction txn = Transaction.currentTxn(); + + txn.start(); + SearchCriteria sc = UnmanagedApplianceSearch.create(); + sc.setParameters("lastPinged", lastPingSecondsAfter); sc.setParameters("types", Type.ExternalDhcp, Type.ExternalFirewall, Type.ExternalLoadBalancer, Type.PxeServer, Type.TrafficMonitor, Type.L2Networking); - List hosts = lockRows(sc, null, true); - - for (HostVO host : hosts) { - host.setManagementServerId(managementServerId); - update(host.getId(), host); - } - - txn.commit(); - - return hosts; + List hosts = lockRows(sc, null, true); + + for (HostVO host : hosts) { + host.setManagementServerId(managementServerId); + update(host.getId(), host); + } + + txn.commit(); + + return hosts; } @Override @@ -405,7 +404,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao ub = getUpdateBuilder(host); update(ub, sc, null); } - + @Override public List listByHostTag(Host.Type type, Long clusterId, Long podId, long dcId, String hostTag) { @@ -438,8 +437,8 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao return listBy(sc); } - - + + @Override public List listAllUpAndEnabledNonHAHosts(Type type, Long clusterId, Long podId, long dcId, String haTag) { SearchBuilder hostTagSearch = null; @@ -449,42 +448,42 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao hostTagSearch.or("tagNull", hostTagSearch.entity().getTag(), SearchCriteria.Op.NULL); hostTagSearch.cp(); } - + SearchBuilder hostSearch = createSearchBuilder(); - + hostSearch.and("type", hostSearch.entity().getType(), SearchCriteria.Op.EQ); hostSearch.and("clusterId", hostSearch.entity().getClusterId(), SearchCriteria.Op.EQ); hostSearch.and("podId", hostSearch.entity().getPodId(), SearchCriteria.Op.EQ); hostSearch.and("zoneId", hostSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); hostSearch.and("status", hostSearch.entity().getStatus(), SearchCriteria.Op.EQ); hostSearch.and("resourceState", hostSearch.entity().getResourceState(), SearchCriteria.Op.EQ); - + if (haTag != null && !haTag.isEmpty()) { hostSearch.join("hostTagSearch", hostTagSearch, hostSearch.entity().getId(), hostTagSearch.entity().getHostId(), JoinBuilder.JoinType.LEFTOUTER); } SearchCriteria sc = hostSearch.create(); - + if (haTag != null && !haTag.isEmpty()) { sc.setJoinParameters("hostTagSearch", "tag", haTag); } - + if (type != null) { sc.setParameters("type", type); } - + if (clusterId != null) { sc.setParameters("clusterId", clusterId); } - + if (podId != null) { sc.setParameters("podId", podId); } - + sc.setParameters("zoneId", dcId); sc.setParameters("status", Status.Up); sc.setParameters("resourceState", ResourceState.Enabled); - + return listBy(sc); } @@ -531,7 +530,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao } return result; } - + @Override public void saveDetails(HostVO host) { Map details = host.getDetails(); @@ -653,81 +652,81 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao } - @Override - public boolean updateState(Status oldStatus, Event event, Status newStatus, Host vo, Object data) { - HostVO host = findById(vo.getId()); - long oldPingTime = host.getLastPinged(); + @Override + public boolean updateState(Status oldStatus, Event event, Status newStatus, Host vo, Object data) { + HostVO host = findById(vo.getId()); + long oldPingTime = host.getLastPinged(); - SearchBuilder sb = createSearchBuilder(); - sb.and("status", sb.entity().getStatus(), SearchCriteria.Op.EQ); - sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); - sb.and("update", sb.entity().getUpdated(), SearchCriteria.Op.EQ); - if (newStatus.checkManagementServer()) { - sb.and("ping", sb.entity().getLastPinged(), SearchCriteria.Op.EQ); - sb.and().op("nullmsid", sb.entity().getManagementServerId(), SearchCriteria.Op.NULL); - sb.or("msid", sb.entity().getManagementServerId(), SearchCriteria.Op.EQ); - sb.closeParen(); - } - sb.done(); + SearchBuilder sb = createSearchBuilder(); + sb.and("status", sb.entity().getStatus(), SearchCriteria.Op.EQ); + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("update", sb.entity().getUpdated(), SearchCriteria.Op.EQ); + if (newStatus.checkManagementServer()) { + sb.and("ping", sb.entity().getLastPinged(), SearchCriteria.Op.EQ); + sb.and().op("nullmsid", sb.entity().getManagementServerId(), SearchCriteria.Op.NULL); + sb.or("msid", sb.entity().getManagementServerId(), SearchCriteria.Op.EQ); + sb.closeParen(); + } + sb.done(); - SearchCriteria sc = sb.create(); + SearchCriteria sc = sb.create(); - sc.setParameters("status", oldStatus); - sc.setParameters("id", host.getId()); - sc.setParameters("update", host.getUpdated()); - long oldUpdateCount = host.getUpdated(); - if (newStatus.checkManagementServer()) { - sc.setParameters("ping", oldPingTime); - sc.setParameters("msid", host.getManagementServerId()); - } + sc.setParameters("status", oldStatus); + sc.setParameters("id", host.getId()); + sc.setParameters("update", host.getUpdated()); + long oldUpdateCount = host.getUpdated(); + if (newStatus.checkManagementServer()) { + sc.setParameters("ping", oldPingTime); + sc.setParameters("msid", host.getManagementServerId()); + } - long newUpdateCount = host.incrUpdated(); - UpdateBuilder ub = getUpdateBuilder(host); - ub.set(host, _statusAttr, newStatus); - if (newStatus.updateManagementServer()) { - if (newStatus.lostConnection()) { - ub.set(host, _msIdAttr, null); - } else { - ub.set(host, _msIdAttr, host.getManagementServerId()); - } - if (event.equals(Event.Ping) || event.equals(Event.AgentConnected)) { - ub.set(host, _pingTimeAttr, System.currentTimeMillis() >> 10); - } - } - if (event.equals(Event.ManagementServerDown)) { - ub.set(host, _pingTimeAttr, ((System.currentTimeMillis() >> 10) - (10 * 60))); - } - int result = update(ub, sc, null); - assert result <= 1 : "How can this update " + result + " rows? "; + long newUpdateCount = host.incrUpdated(); + UpdateBuilder ub = getUpdateBuilder(host); + ub.set(host, _statusAttr, newStatus); + if (newStatus.updateManagementServer()) { + if (newStatus.lostConnection()) { + ub.set(host, _msIdAttr, null); + } else { + ub.set(host, _msIdAttr, host.getManagementServerId()); + } + if (event.equals(Event.Ping) || event.equals(Event.AgentConnected)) { + ub.set(host, _pingTimeAttr, System.currentTimeMillis() >> 10); + } + } + if (event.equals(Event.ManagementServerDown)) { + ub.set(host, _pingTimeAttr, ((System.currentTimeMillis() >> 10) - (10 * 60))); + } + int result = update(ub, sc, null); + assert result <= 1 : "How can this update " + result + " rows? "; - if (status_logger.isDebugEnabled() && result == 0) { - HostVO ho = findById(host.getId()); - assert ho != null : "How how how? : " + host.getId(); + if (status_logger.isDebugEnabled() && result == 0) { + HostVO ho = findById(host.getId()); + assert ho != null : "How how how? : " + host.getId(); + + StringBuilder str = new StringBuilder("Unable to update host for event:").append(event.toString()); + str.append(". Name=").append(host.getName()); + str.append("; New=[status=").append(newStatus.toString()).append(":msid=") + .append(newStatus.lostConnection() ? "null" : host.getManagementServerId()).append(":lastpinged=").append(host.getLastPinged()).append("]"); + str.append("; Old=[status=").append(oldStatus.toString()).append(":msid=").append(host.getManagementServerId()).append(":lastpinged=") + .append(oldPingTime).append("]"); + str.append("; DB=[status=").append(vo.getStatus().toString()).append(":msid=").append(vo.getManagementServerId()).append(":lastpinged=") + .append(vo.getLastPinged()).append(":old update count=").append(oldUpdateCount).append("]"); + status_logger.debug(str.toString()); + } else { + StringBuilder msg = new StringBuilder("Agent status update: ["); + msg.append("id = " + host.getId()); + msg.append("; name = " + host.getName()); + msg.append("; old status = " + oldStatus); + msg.append("; event = " + event); + msg.append("; new status = " + newStatus); + msg.append("; old update count = " + oldUpdateCount); + msg.append("; new update count = " + newUpdateCount + "]"); + status_logger.debug(msg.toString()); + } + + return result > 0; + } - StringBuilder str = new StringBuilder("Unable to update host for event:").append(event.toString()); - str.append(". Name=").append(host.getName()); - str.append("; New=[status=").append(newStatus.toString()).append(":msid=") - .append(newStatus.lostConnection() ? "null" : host.getManagementServerId()).append(":lastpinged=").append(host.getLastPinged()).append("]"); - str.append("; Old=[status=").append(oldStatus.toString()).append(":msid=").append(host.getManagementServerId()).append(":lastpinged=") - .append(oldPingTime).append("]"); - str.append("; DB=[status=").append(vo.getStatus().toString()).append(":msid=").append(vo.getManagementServerId()).append(":lastpinged=") - .append(vo.getLastPinged()).append(":old update count=").append(oldUpdateCount).append("]"); - status_logger.debug(str.toString()); - } else { - StringBuilder msg = new StringBuilder("Agent status update: ["); - msg.append("id = " + host.getId()); - msg.append("; name = " + host.getName()); - msg.append("; old status = " + oldStatus); - msg.append("; event = " + event); - msg.append("; new status = " + newStatus); - msg.append("; old update count = " + oldUpdateCount); - msg.append("; new update count = " + newUpdateCount + "]"); - status_logger.debug(msg.toString()); - } - - return result > 0; - } - @Override public boolean updateResourceState(ResourceState oldState, ResourceState.Event event, ResourceState newState, Host vo) { HostVO host = (HostVO)vo; @@ -735,41 +734,41 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao sb.and("resource_state", sb.entity().getResourceState(), SearchCriteria.Op.EQ); sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); sb.done(); - + SearchCriteria sc = sb.create(); sc.setParameters("resource_state", oldState); sc.setParameters("id", host.getId()); - + UpdateBuilder ub = getUpdateBuilder(host); ub.set(host, _resourceStateAttr, newState); int result = update(ub, sc, null); assert result <= 1 : "How can this update " + result + " rows? "; - + if (state_logger.isDebugEnabled() && result == 0) { HostVO ho = findById(host.getId()); assert ho != null : "How how how? : " + host.getId(); StringBuilder str = new StringBuilder("Unable to update resource state: ["); - str.append("m = " + host.getId()); - str.append("; name = " + host.getName()); - str.append("; old state = " + oldState); - str.append("; event = " + event); - str.append("; new state = " + newState + "]"); - state_logger.debug(str.toString()); + str.append("m = " + host.getId()); + str.append("; name = " + host.getName()); + str.append("; old state = " + oldState); + str.append("; event = " + event); + str.append("; new state = " + newState + "]"); + state_logger.debug(str.toString()); } else { - StringBuilder msg = new StringBuilder("Resource state update: ["); - msg.append("id = " + host.getId()); - msg.append("; name = " + host.getName()); - msg.append("; old state = " + oldState); - msg.append("; event = " + event); - msg.append("; new state = " + newState + "]"); - state_logger.debug(msg.toString()); + StringBuilder msg = new StringBuilder("Resource state update: ["); + msg.append("id = " + host.getId()); + msg.append("; name = " + host.getName()); + msg.append("; old state = " + oldState); + msg.append("; event = " + event); + msg.append("; new state = " + newState + "]"); + state_logger.debug(msg.toString()); } - + return result > 0; } - + @Override public HostVO findByTypeNameAndZoneId(long zoneId, String name, Host.Type type) { SearchCriteria sc = TypeNameZoneSearch.create(); @@ -779,15 +778,15 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao return findOneBy(sc); } - @Override - public List findHypervisorHostInCluster(long clusterId) { - SearchCriteria sc = TypeClusterStatusSearch.create(); - sc.setParameters("type", Host.Type.Routing); - sc.setParameters("cluster", clusterId); - sc.setParameters("status", Status.Up); - sc.setParameters("resourceState", ResourceState.Enabled); - - return listBy(sc); - } + @Override + public List findHypervisorHostInCluster(long clusterId) { + SearchCriteria sc = TypeClusterStatusSearch.create(); + sc.setParameters("type", Host.Type.Routing); + sc.setParameters("cluster", clusterId); + sc.setParameters("status", Status.Up); + sc.setParameters("resourceState", ResourceState.Enabled); + + return listBy(sc); + } } diff --git a/server/src/com/cloud/hypervisor/HypervisorGuruManagerImpl.java b/server/src/com/cloud/hypervisor/HypervisorGuruManagerImpl.java index 5a32de744a0..b0c25c74166 100644 --- a/server/src/com/cloud/hypervisor/HypervisorGuruManagerImpl.java +++ b/server/src/com/cloud/hypervisor/HypervisorGuruManagerImpl.java @@ -33,8 +33,6 @@ import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.utils.component.Adapters; -import com.cloud.utils.component.ComponentLocator; @Component @Local(value = { HypervisorGuruManager.class } ) @@ -42,54 +40,56 @@ public class HypervisorGuruManagerImpl implements HypervisorGuruManager { public static final Logger s_logger = Logger.getLogger(HypervisorGuruManagerImpl.class.getName()); @Inject HostDao _hostDao; - - String _name; - - @Inject List _hvGuruList; + + String _name; + + @Inject List _hvGuruList; Map _hvGurus = new HashMap(); - - @Override - public boolean configure(String name, Map params) throws ConfigurationException { - _name = name; - return true; - } - - @PostConstruct - public void init() { - for(HypervisorGuru guru : _hvGuruList) { - _hvGurus.put(guru.getHypervisorType(), guru); - } - } - @Override - public boolean start() { - return true; - } + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + return true; + } - @Override - public boolean stop() { - return true; - } + @PostConstruct + public void init() { + for(HypervisorGuru guru : _hvGuruList) { + _hvGurus.put(guru.getHypervisorType(), guru); + } + } - @Override - public String getName() { - return _name; - } + @Override + public boolean start() { + return true; + } - public HypervisorGuru getGuru(HypervisorType hypervisorType) { - return _hvGurus.get(hypervisorType); - } - + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public HypervisorGuru getGuru(HypervisorType hypervisorType) { + return _hvGurus.get(hypervisorType); + } + + @Override public long getGuruProcessedCommandTargetHost(long hostId, Command cmd) { - HostVO hostVo = _hostDao.findById(hostId); - HypervisorGuru hvGuru = null; - if(hostVo.getType() == Host.Type.Routing) { - hvGuru = _hvGurus.get(hostVo.getHypervisorType()); - } - - if(hvGuru != null) - return hvGuru.getCommandHostDelegation(hostId, cmd); - - return hostId; + HostVO hostVo = _hostDao.findById(hostId); + HypervisorGuru hvGuru = null; + if(hostVo.getType() == Host.Type.Routing) { + hvGuru = _hvGurus.get(hostVo.getHypervisorType()); + } + + if(hvGuru != null) + return hvGuru.getCommandHostDelegation(hostId, cmd); + + return hostId; } } diff --git a/server/src/com/cloud/maint/UpgradeManagerImpl.java b/server/src/com/cloud/maint/UpgradeManagerImpl.java index 2f50dffad71..03c7d2f0cea 100644 --- a/server/src/com/cloud/maint/UpgradeManagerImpl.java +++ b/server/src/com/cloud/maint/UpgradeManagerImpl.java @@ -36,15 +36,14 @@ import javax.naming.ConfigurationException; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; -import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; +import org.apache.commons.httpclient.methods.GetMethod; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.maint.dao.AgentUpgradeDao; import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.component.ComponentLocator; /** * @@ -57,7 +56,7 @@ import com.cloud.utils.component.ComponentLocator; @Component @Local(UpgradeManager.class) public class UpgradeManagerImpl implements UpgradeManager { - private final static Logger s_logger = Logger.getLogger(UpgradeManagerImpl.class); + private final static Logger s_logger = Logger.getLogger(UpgradeManagerImpl.class); private static final MultiThreadedHttpConnectionManager s_httpClientManager = new MultiThreadedHttpConnectionManager(); String _name; @@ -66,10 +65,10 @@ public class UpgradeManagerImpl implements UpgradeManager { // String _upgradeUrl; String _agentPath; long _checkInterval; - + @Inject AgentUpgradeDao _upgradeDao; @Inject ConfigurationDao _configDao; - + @Override public State registerForUpgrade(long hostId, String version) { State state = State.UpToDate; @@ -90,11 +89,11 @@ public class UpgradeManagerImpl implements UpgradeManager { _upgradeDao.persist(vo); } } - */ - + */ + return state; } - + public String deployNewAgent(String url) { s_logger.info("Updating agent with binary from " + url); @@ -132,18 +131,18 @@ public class UpgradeManagerImpl implements UpgradeManager { s_logger.debug("New Agent zip file is now retrieved"); } catch (final HttpException e) { - return "Unable to retrieve the file from " + url; + return "Unable to retrieve the file from " + url; } catch (final IOException e) { - return "Unable to retrieve the file from " + url; + return "Unable to retrieve the file from " + url; } finally { - method.releaseConnection(); + method.releaseConnection(); } - + file.delete(); - + return "File will be deployed."; } - + // @Override // public String getAgentUrl() { // return _upgradeUrl; @@ -174,7 +173,7 @@ public class UpgradeManagerImpl implements UpgradeManager { } //_upgradeUrl = configs.get("upgrade.url"); - + // if (_upgradeUrl == null) { // s_logger.debug("There is no upgrade url found in configuration table"); // // _upgradeUrl = "http://updates.vmops.com/releases/rss.xml"; diff --git a/server/src/com/cloud/migration/Db21to22MigrationUtil.java b/server/src/com/cloud/migration/Db21to22MigrationUtil.java index b6ff9069ed3..66a7d59f53a 100755 --- a/server/src/com/cloud/migration/Db21to22MigrationUtil.java +++ b/server/src/com/cloud/migration/Db21to22MigrationUtil.java @@ -19,9 +19,7 @@ package com.cloud.migration; import java.io.File; import java.sql.PreparedStatement; import java.sql.ResultSet; -import java.util.LinkedList; import java.util.List; -import java.util.Queue; import javax.inject.Inject; @@ -32,21 +30,17 @@ import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.ResourceCountVO; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.configuration.dao.ResourceCountDao; -import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; -import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; -import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.resource.ResourceManager; import com.cloud.user.Account; import com.cloud.user.dao.AccountDao; import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; @@ -56,37 +50,37 @@ import com.cloud.vm.dao.InstanceGroupDao; import com.cloud.vm.dao.InstanceGroupVMMapDao; public class Db21to22MigrationUtil { - - @Inject private ClusterDao _clusterDao; - @Inject private HostDao _hostDao; - @Inject private AccountDao _accountDao; - @Inject private DomainDao _domainDao; - @Inject private ResourceCountDao _resourceCountDao; - @Inject private InstanceGroupDao _vmGroupDao; - @Inject private InstanceGroupVMMapDao _groupVMMapDao; - @Inject private ConfigurationDao _configurationDao; - @Inject private DataCenterDao _zoneDao; - @Inject private ResourceManager _resourceMgr; - + + @Inject private ClusterDao _clusterDao; + @Inject private HostDao _hostDao; + @Inject private AccountDao _accountDao; + @Inject private DomainDao _domainDao; + @Inject private ResourceCountDao _resourceCountDao; + @Inject private InstanceGroupDao _vmGroupDao; + @Inject private InstanceGroupVMMapDao _groupVMMapDao; + @Inject private ConfigurationDao _configurationDao; + @Inject private DataCenterDao _zoneDao; + @Inject private ResourceManager _resourceMgr; + private void doMigration() { setupComponents(); migrateResourceCounts(); - + setupInstanceGroups(); migrateZones(); - + setupClusterGuid(); - + System.out.println("Migration done"); } - + /* add guid in cluster table */ private void setupClusterGuid() { - - //FIXME moving out XenServer code out of server. This upgrade step need to be taken care of - /* + + //FIXME moving out XenServer code out of server. This upgrade step need to be taken care of + /* XenServerConnectionPool _connPool = XenServerConnectionPool.getInstance(); List clusters = _clusterDao.listByHyTypeWithoutGuid(HypervisorType.XenServer.toString()); for (ClusterVO cluster : clusters) { @@ -120,34 +114,34 @@ public class Db21to22MigrationUtil { break; } } - */ + */ } - + /** * This method migrates the zones based on bug: 7204 * based on the param direct.attach.untagged.vlan.enabled, we update zone to basic or advanced for 2.2 */ private void migrateZones(){ - try { - System.out.println("Migrating zones"); - String val = _configurationDao.getValue("direct.attach.untagged.vlan.enabled"); - NetworkType networkType; - if(val == null || val.equalsIgnoreCase("true")){ - networkType = NetworkType.Basic; - }else{ - networkType = NetworkType.Advanced; - } - List existingZones = _zoneDao.listAll(); - for(DataCenterVO zone : existingZones){ - zone.setNetworkType(networkType); - _zoneDao.update(zone.getId(), zone); - } - } catch (Exception e) { - System.out.println("Unhandled exception in migrateZones()" + e); - } + try { + System.out.println("Migrating zones"); + String val = _configurationDao.getValue("direct.attach.untagged.vlan.enabled"); + NetworkType networkType; + if(val == null || val.equalsIgnoreCase("true")){ + networkType = NetworkType.Basic; + }else{ + networkType = NetworkType.Advanced; + } + List existingZones = _zoneDao.listAll(); + for(DataCenterVO zone : existingZones){ + zone.setNetworkType(networkType); + _zoneDao.update(zone.getId(), zone); + } + } catch (Exception e) { + System.out.println("Unhandled exception in migrateZones()" + e); + } } - + private void migrateResourceCounts() { System.out.println("migrating resource counts"); SearchBuilder sb = _resourceCountDao.createSearchBuilder(); @@ -175,46 +169,46 @@ public class Db21to22MigrationUtil { private void setupComponents() { } - + private void setupInstanceGroups() { - System.out.println("setting up vm instance groups"); - - //Search for all the vms that have not null groups - Long vmId = 0L; - Long accountId = 0L; - String groupName; - Transaction txn = Transaction.open(Transaction.CLOUD_DB); - txn.start(); - try { - String request = "SELECT vm.id, uservm.account_id, vm.group from vm_instance vm, user_vm uservm where vm.group is not null and vm.removed is null and vm.id=uservm.id order by id"; - System.out.println(request); - PreparedStatement statement = txn.prepareStatement(request); - ResultSet result = statement.executeQuery(); - while (result.next()) { - vmId = result.getLong(1); - accountId = result.getLong(2); - groupName = result.getString(3); - InstanceGroupVO group = _vmGroupDao.findByAccountAndName(accountId, groupName); - //Create vm group if the group doesn't exist for this account - if (group == null) { - group = new InstanceGroupVO(groupName, accountId); - group = _vmGroupDao.persist(group); - System.out.println("Created new isntance group with name " + groupName + " for account id=" + accountId); - } - - if (group != null) { - InstanceGroupVMMapVO groupVmMapVO = new InstanceGroupVMMapVO(group.getId(), vmId); - _groupVMMapDao.persist(groupVmMapVO); - System.out.println("Assigned vm id=" + vmId + " to group with name " + groupName + " for account id=" + accountId); - } - } - txn.commit(); - statement.close(); - } catch (Exception e) { - System.out.println("Unhandled exception: " + e); - } finally { - txn.close(); - } + System.out.println("setting up vm instance groups"); + + //Search for all the vms that have not null groups + Long vmId = 0L; + Long accountId = 0L; + String groupName; + Transaction txn = Transaction.open(Transaction.CLOUD_DB); + txn.start(); + try { + String request = "SELECT vm.id, uservm.account_id, vm.group from vm_instance vm, user_vm uservm where vm.group is not null and vm.removed is null and vm.id=uservm.id order by id"; + System.out.println(request); + PreparedStatement statement = txn.prepareStatement(request); + ResultSet result = statement.executeQuery(); + while (result.next()) { + vmId = result.getLong(1); + accountId = result.getLong(2); + groupName = result.getString(3); + InstanceGroupVO group = _vmGroupDao.findByAccountAndName(accountId, groupName); + //Create vm group if the group doesn't exist for this account + if (group == null) { + group = new InstanceGroupVO(groupName, accountId); + group = _vmGroupDao.persist(group); + System.out.println("Created new isntance group with name " + groupName + " for account id=" + accountId); + } + + if (group != null) { + InstanceGroupVMMapVO groupVmMapVO = new InstanceGroupVMMapVO(group.getId(), vmId); + _groupVMMapDao.persist(groupVmMapVO); + System.out.println("Assigned vm id=" + vmId + " to group with name " + groupName + " for account id=" + accountId); + } + } + txn.commit(); + statement.close(); + } catch (Exception e) { + System.out.println("Unhandled exception: " + e); + } finally { + txn.close(); + } } diff --git a/server/src/com/cloud/network/ExteralIpAddressAllocator.java b/server/src/com/cloud/network/ExteralIpAddressAllocator.java index f8b5c152519..eca5ff6eda9 100644 --- a/server/src/com/cloud/network/ExteralIpAddressAllocator.java +++ b/server/src/com/cloud/network/ExteralIpAddressAllocator.java @@ -19,7 +19,6 @@ package com.cloud.network; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; @@ -33,136 +32,135 @@ import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.configuration.dao.ConfigurationDao; - import com.cloud.dc.dao.VlanDao; import com.cloud.network.dao.IPAddressDao; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.exception.CloudRuntimeException; @Component @Local(value=IpAddrAllocator.class) public class ExteralIpAddressAllocator implements IpAddrAllocator{ - private static final Logger s_logger = Logger.getLogger(ExteralIpAddressAllocator.class); - String _name; + private static final Logger s_logger = Logger.getLogger(ExteralIpAddressAllocator.class); + String _name; @Inject ConfigurationDao _configDao = null; @Inject IPAddressDao _ipAddressDao = null; @Inject VlanDao _vlanDao; - private boolean _isExternalIpAllocatorEnabled = false; - private String _externalIpAllocatorUrl = null; + private boolean _isExternalIpAllocatorEnabled = false; + private String _externalIpAllocatorUrl = null; - - @Override - public IpAddr getPrivateIpAddress(String macAddr, long dcId, long podId) { - if (_externalIpAllocatorUrl == null || this._externalIpAllocatorUrl.equalsIgnoreCase("")) { - return new IpAddr(); - } - String urlString = this._externalIpAllocatorUrl + "?command=getIpAddr&mac=" + macAddr + "&dc=" + dcId + "&pod=" + podId; - s_logger.debug("getIP:" + urlString); - - BufferedReader in = null; - try { - URL url = new URL(urlString); - URLConnection conn = url.openConnection(); - conn.setReadTimeout(30000); - - in = new BufferedReader(new InputStreamReader(conn.getInputStream())); - String inputLine; - while ((inputLine = in.readLine()) != null) { - s_logger.debug(inputLine); - String[] tokens = inputLine.split(","); - if (tokens.length != 3) { - s_logger.debug("the return value should be: mac,netmask,gateway"); - return new IpAddr(); - } - return new IpAddr(tokens[0], tokens[1], tokens[2]); - } - - return new IpAddr(); - } catch (MalformedURLException e) { - throw new CloudRuntimeException("URL is malformed " + urlString, e); - } catch (IOException e) { - return new IpAddr(); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - } - } - } - - } - - @Override - public IpAddr getPublicIpAddress(String macAddr, long dcId, long podId) { - /*TODO: call API to get ip address from external DHCP server*/ - return getPrivateIpAddress(macAddr, dcId, podId); - } - - @Override - public boolean releasePrivateIpAddress(String ip, long dcId, long podId) { - /*TODO: call API to release the ip address from external DHCP server*/ - if (_externalIpAllocatorUrl == null || this._externalIpAllocatorUrl.equalsIgnoreCase("")) { - return false; - } - - String urlString = this._externalIpAllocatorUrl + "?command=releaseIpAddr&ip=" + ip + "&dc=" + dcId + "&pod=" + podId; - - s_logger.debug("releaseIP:" + urlString); - BufferedReader in = null; - try { - URL url = new URL(urlString); - URLConnection conn = url.openConnection(); - conn.setReadTimeout(30000); - - in = new BufferedReader(new InputStreamReader(conn.getInputStream())); - - return true; - } catch (MalformedURLException e) { - throw new CloudRuntimeException("URL is malformed " + urlString, e); - } catch (IOException e) { - return false; - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - } - } - } - } - - @Override - public boolean releasePublicIpAddress(String ip, long dcId, long podId) { - /*TODO: call API to release the ip address from external DHCP server*/ - return releasePrivateIpAddress(ip, dcId, podId); - } - - public boolean exteralIpAddressAllocatorEnabled() { - return _isExternalIpAllocatorEnabled; - } - - @Override - public boolean configure(String name, Map params) throws ConfigurationException { - _isExternalIpAllocatorEnabled = Boolean.parseBoolean(_configDao.getValue("direct.attach.network.externalIpAllocator.enabled")); - _externalIpAllocatorUrl = _configDao.getValue("direct.attach.network.externalIpAllocator.url"); - _name = name; - - return true; - } - @Override - public String getName() { - return _name; - } + @Override + public IpAddr getPrivateIpAddress(String macAddr, long dcId, long podId) { + if (_externalIpAllocatorUrl == null || this._externalIpAllocatorUrl.equalsIgnoreCase("")) { + return new IpAddr(); + } + String urlString = this._externalIpAllocatorUrl + "?command=getIpAddr&mac=" + macAddr + "&dc=" + dcId + "&pod=" + podId; + s_logger.debug("getIP:" + urlString); - @Override - public boolean start() { - return true; - } + BufferedReader in = null; + try { + URL url = new URL(urlString); + URLConnection conn = url.openConnection(); + conn.setReadTimeout(30000); - @Override - public boolean stop() { - return true; - } + in = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String inputLine; + while ((inputLine = in.readLine()) != null) { + s_logger.debug(inputLine); + String[] tokens = inputLine.split(","); + if (tokens.length != 3) { + s_logger.debug("the return value should be: mac,netmask,gateway"); + return new IpAddr(); + } + return new IpAddr(tokens[0], tokens[1], tokens[2]); + } + + return new IpAddr(); + } catch (MalformedURLException e) { + throw new CloudRuntimeException("URL is malformed " + urlString, e); + } catch (IOException e) { + return new IpAddr(); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + + } + + @Override + public IpAddr getPublicIpAddress(String macAddr, long dcId, long podId) { + /*TODO: call API to get ip address from external DHCP server*/ + return getPrivateIpAddress(macAddr, dcId, podId); + } + + @Override + public boolean releasePrivateIpAddress(String ip, long dcId, long podId) { + /*TODO: call API to release the ip address from external DHCP server*/ + if (_externalIpAllocatorUrl == null || this._externalIpAllocatorUrl.equalsIgnoreCase("")) { + return false; + } + + String urlString = this._externalIpAllocatorUrl + "?command=releaseIpAddr&ip=" + ip + "&dc=" + dcId + "&pod=" + podId; + + s_logger.debug("releaseIP:" + urlString); + BufferedReader in = null; + try { + URL url = new URL(urlString); + URLConnection conn = url.openConnection(); + conn.setReadTimeout(30000); + + in = new BufferedReader(new InputStreamReader(conn.getInputStream())); + + return true; + } catch (MalformedURLException e) { + throw new CloudRuntimeException("URL is malformed " + urlString, e); + } catch (IOException e) { + return false; + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + } + + @Override + public boolean releasePublicIpAddress(String ip, long dcId, long podId) { + /*TODO: call API to release the ip address from external DHCP server*/ + return releasePrivateIpAddress(ip, dcId, podId); + } + + @Override + public boolean exteralIpAddressAllocatorEnabled() { + return _isExternalIpAllocatorEnabled; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _isExternalIpAllocatorEnabled = Boolean.parseBoolean(_configDao.getValue("direct.attach.network.externalIpAllocator.enabled")); + _externalIpAllocatorUrl = _configDao.getValue("direct.attach.network.externalIpAllocator.url"); + _name = name; + + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } } diff --git a/server/src/com/cloud/network/ExternalNetworkDeviceManagerImpl.java b/server/src/com/cloud/network/ExternalNetworkDeviceManagerImpl.java index 93e0ef6d01d..e95dd175fc8 100755 --- a/server/src/com/cloud/network/ExternalNetworkDeviceManagerImpl.java +++ b/server/src/com/cloud/network/ExternalNetworkDeviceManagerImpl.java @@ -27,22 +27,21 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.admin.network.AddNetworkDeviceCmd; +import org.apache.cloudstack.api.command.admin.network.DeleteNetworkDeviceCmd; import org.apache.cloudstack.api.command.admin.network.ListNetworkDeviceCmd; +import org.apache.cloudstack.api.response.NetworkDeviceResponse; import org.apache.cloudstack.network.ExternalNetworkDeviceManager; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; import com.cloud.api.ApiDBUtils; - -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.IdentityService; -import org.apache.cloudstack.api.command.admin.network.DeleteNetworkDeviceCmd; import com.cloud.baremetal.ExternalDhcpManager; import com.cloud.baremetal.PxeServerManager; -import com.cloud.baremetal.PxeServerProfile; import com.cloud.baremetal.PxeServerManager.PxeServerType; +import com.cloud.baremetal.PxeServerProfile; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.DataCenter; import com.cloud.dc.Pod; @@ -64,14 +63,11 @@ import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; import com.cloud.network.dao.VpnUserDao; import com.cloud.network.rules.dao.PortForwardingRulesDao; import com.cloud.offerings.dao.NetworkOfferingDao; -import com.cloud.server.ManagementServer; -import org.apache.cloudstack.api.response.NetworkDeviceResponse; import com.cloud.server.api.response.NwDeviceDhcpResponse; import com.cloud.server.api.response.PxePingResponse; import com.cloud.user.AccountManager; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserStatisticsDao; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.NicDao; @@ -112,16 +108,16 @@ public class ExternalNetworkDeviceManagerImpl implements ExternalNetworkDeviceMa // obsolete // private final static IdentityService _identityService = (IdentityService)ComponentLocator.getLocator(ManagementServer.Name).getManager(IdentityService.class); - + private static final org.apache.log4j.Logger s_logger = Logger.getLogger(ExternalNetworkDeviceManagerImpl.class); protected String _name; - + @Override public boolean configure(String name, Map params) throws ConfigurationException { _name = name; return true; } - + @Override public boolean start() { return true; @@ -136,21 +132,21 @@ public class ExternalNetworkDeviceManagerImpl implements ExternalNetworkDeviceMa public String getName() { return _name; } - + @Override public Host addNetworkDevice(AddNetworkDeviceCmd cmd) { Map paramList = cmd.getParamList(); if (paramList == null) { throw new CloudRuntimeException("Parameter list is null"); } - + Collection paramsCollection = paramList.values(); HashMap params = (HashMap) (paramsCollection.toArray())[0]; if (cmd.getDeviceType().equalsIgnoreCase(NetworkDevice.ExternalDhcp.getName())) { //Long zoneId = _identityService.getIdentityId("data_center", (String) params.get(ApiConstants.ZONE_ID)); //Long podId = _identityService.getIdentityId("host_pod_ref", (String)params.get(ApiConstants.POD_ID)); - Long zoneId = Long.valueOf((String) params.get(ApiConstants.ZONE_ID)); - Long podId = Long.valueOf((String)params.get(ApiConstants.POD_ID)); + Long zoneId = Long.valueOf((String) params.get(ApiConstants.ZONE_ID)); + Long podId = Long.valueOf((String)params.get(ApiConstants.POD_ID)); String type = (String) params.get(ApiConstants.DHCP_SERVER_TYPE); String url = (String) params.get(ApiConstants.URL); String username = (String) params.get(ApiConstants.USERNAME); @@ -217,7 +213,7 @@ public class ExternalNetworkDeviceManagerImpl implements ExternalNetworkDeviceMa } else { throw new CloudRuntimeException("Unsupported network device type:" + host.getType()); } - + response.setId(device.getUuid()); return response; } @@ -234,19 +230,19 @@ public class ExternalNetworkDeviceManagerImpl implements ExternalNetworkDeviceMa // } else { // List devs = _hostDao.listBy(type, zoneId); // res.addAll(devs); - // } - - // return res; + // } + + // return res; return null; } - + @Override public List listNetworkDevice(ListNetworkDeviceCmd cmd) { Map paramList = cmd.getParamList(); if (paramList == null) { throw new CloudRuntimeException("Parameter list is null"); } - + List res; Collection paramsCollection = paramList.values(); HashMap params = (HashMap) (paramsCollection.toArray())[0]; @@ -275,13 +271,13 @@ public class ExternalNetworkDeviceManagerImpl implements ExternalNetworkDeviceMa } else { throw new CloudRuntimeException("Unknown network device type:" + cmd.getDeviceType()); } - + return res; } @Override public boolean deleteNetworkDevice(DeleteNetworkDeviceCmd cmd) { - HostVO device = _hostDao.findById(cmd.getId()); - return true; + HostVO device = _hostDao.findById(cmd.getId()); + return true; } } diff --git a/server/src/com/cloud/network/as/AutoScaleManagerImpl.java b/server/src/com/cloud/network/as/AutoScaleManagerImpl.java index d49f4aa168e..7c8a6f5a800 100644 --- a/server/src/com/cloud/network/as/AutoScaleManagerImpl.java +++ b/server/src/com/cloud/network/as/AutoScaleManagerImpl.java @@ -23,27 +23,31 @@ import java.util.List; import java.util.Map; import javax.ejb.Local; +import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.acl.ControlledEntity; -import org.apache.cloudstack.api.command.admin.autoscale.CreateCounterCmd; -import org.apache.cloudstack.api.command.user.autoscale.*; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.ApiConstants; -import com.cloud.api.ApiDBUtils; -import com.cloud.api.ApiDispatcher; import org.apache.cloudstack.api.BaseListAccountResourcesCmd; +import org.apache.cloudstack.api.command.admin.autoscale.CreateCounterCmd; import org.apache.cloudstack.api.command.user.autoscale.CreateAutoScalePolicyCmd; import org.apache.cloudstack.api.command.user.autoscale.CreateAutoScaleVmGroupCmd; +import org.apache.cloudstack.api.command.user.autoscale.CreateAutoScaleVmProfileCmd; import org.apache.cloudstack.api.command.user.autoscale.CreateConditionCmd; -import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; +import org.apache.cloudstack.api.command.user.autoscale.ListAutoScalePoliciesCmd; import org.apache.cloudstack.api.command.user.autoscale.ListAutoScaleVmGroupsCmd; +import org.apache.cloudstack.api.command.user.autoscale.ListAutoScaleVmProfilesCmd; import org.apache.cloudstack.api.command.user.autoscale.ListConditionsCmd; +import org.apache.cloudstack.api.command.user.autoscale.ListCountersCmd; import org.apache.cloudstack.api.command.user.autoscale.UpdateAutoScalePolicyCmd; +import org.apache.cloudstack.api.command.user.autoscale.UpdateAutoScaleVmGroupCmd; import org.apache.cloudstack.api.command.user.autoscale.UpdateAutoScaleVmProfileCmd; +import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; +import org.apache.log4j.Logger; import org.springframework.stereotype.Component; +import com.cloud.api.ApiDBUtils; +import com.cloud.api.ApiDispatcher; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.dao.ConfigurationDao; @@ -81,7 +85,6 @@ import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; -import com.cloud.utils.component.Inject; import com.cloud.utils.component.Manager; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; diff --git a/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java b/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java index e5ee604a2dd..0c79676b8e1 100644 --- a/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java +++ b/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java @@ -32,7 +32,6 @@ import com.cloud.network.rules.FirewallRule.TrafficType; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.server.ResourceTag.TaggedResourceType; import com.cloud.tags.dao.ResourceTagsDaoImpl; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; @@ -286,11 +285,12 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i if (purpose != null) { sc.setParameters("purpose", purpose); } - + sc.setParameters("trafficType", trafficType); return listBy(sc); } + @Override @DB public boolean remove(Long id) { Transaction txn = Transaction.currentTxn(); @@ -316,7 +316,7 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i public List listByIpAndPurposeWithState(Long ipId, Purpose purpose, State state) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("ipId", ipId); - + if (state != null) { sc.setParameters("state", state); } diff --git a/server/src/com/cloud/network/dao/IPAddressDaoImpl.java b/server/src/com/cloud/network/dao/IPAddressDaoImpl.java index 833c8314ca8..4d6bd080e2a 100755 --- a/server/src/com/cloud/network/dao/IPAddressDaoImpl.java +++ b/server/src/com/cloud/network/dao/IPAddressDaoImpl.java @@ -35,7 +35,6 @@ import com.cloud.network.IPAddressVO; import com.cloud.network.IpAddress.State; import com.cloud.server.ResourceTag.TaggedResourceType; import com.cloud.tags.dao.ResourceTagsDaoImpl; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; @@ -62,11 +61,11 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen @Inject protected VlanDaoImpl _vlanDao; protected GenericSearchBuilder CountFreePublicIps; @Inject ResourceTagsDaoImpl _tagsDao; - + // make it public for JUnit test public IPAddressDaoImpl() { } - + @PostConstruct public void init() { AllFieldsSearch = createSearchBuilder(); @@ -101,7 +100,7 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen AllocatedIpCount.and("vlan", AllocatedIpCount.entity().getVlanId(), Op.EQ); AllocatedIpCount.and("allocated", AllocatedIpCount.entity().getAllocatedTime(), Op.NNULL); AllocatedIpCount.done(); - + AllIpCountForDashboard = createSearchBuilder(Integer.class); AllIpCountForDashboard.select(null, Func.COUNT, AllIpCountForDashboard.entity().getAddress()); AllIpCountForDashboard.and("dc", AllIpCountForDashboard.entity().getDataCenterId(), Op.EQ); @@ -111,7 +110,7 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen virtaulNetworkVlan.and("vlanType", virtaulNetworkVlan.entity().getVlanType(), SearchCriteria.Op.EQ); AllIpCountForDashboard.join("vlan", virtaulNetworkVlan, virtaulNetworkVlan.entity().getId(), - AllIpCountForDashboard.entity().getVlanId(), JoinBuilder.JoinType.INNER); + AllIpCountForDashboard.entity().getVlanId(), JoinBuilder.JoinType.INNER); virtaulNetworkVlan.done(); AllIpCountForDashboard.done(); @@ -121,7 +120,7 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen AllocatedIpCountForAccount.and("allocated", AllocatedIpCountForAccount.entity().getAllocatedTime(), Op.NNULL); AllocatedIpCountForAccount.and("network", AllocatedIpCountForAccount.entity().getAssociatedWithNetworkId(), Op.NNULL); AllocatedIpCountForAccount.done(); - + CountFreePublicIps = createSearchBuilder(Long.class); CountFreePublicIps.select(null, Func.COUNT, null); CountFreePublicIps.and("state", CountFreePublicIps.entity().getState(), SearchCriteria.Op.EQ); @@ -167,14 +166,14 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen sc.setParameters("accountId", accountId); return listBy(sc); } - + @Override public List listByVlanId(long vlanId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("vlan", vlanId); return listBy(sc); } - + @Override public IPAddressVO findByIpAndSourceNetworkId(long networkId, String ipAddress) { SearchCriteria sc = AllFieldsSearch.create(); @@ -197,7 +196,7 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen sc.setParameters("dataCenterId", dcId); return listBy(sc); } - + @Override public List listByDcIdIpAddress(long dcId, String ipAddress) { SearchCriteria sc = AllFieldsSearch.create(); @@ -205,19 +204,19 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen sc.setParameters("ipAddress", ipAddress); return listBy(sc); } - + @Override public List listByAssociatedNetwork(long networkId, Boolean isSourceNat) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("network", networkId); - + if (isSourceNat != null) { sc.setParameters("sourceNat", isSourceNat); } - + return listBy(sc); } - + @Override public List listStaticNatPublicIps(long networkId) { SearchCriteria sc = AllFieldsSearch.create(); @@ -225,12 +224,12 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen sc.setParameters("oneToOneNat", true); return listBy(sc); } - + @Override public IPAddressVO findByAssociatedVmId(long vmId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("associatedWithVmId", vmId); - + return findOneBy(sc); } @@ -248,13 +247,13 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen SearchCriteria sc = AllIpCountForDashboard.create(); sc.setParameters("dc", dcId); if (onlyCountAllocated){ - sc.setParameters("state", State.Free); + sc.setParameters("state", State.Free); } sc.setJoinParameters("vlan", "vlanType", vlanType.toString()); return customSearch(sc, null).get(0); } - + @Override @DB public int countIPs(long dcId, Long accountId, String vlanId, String vlanGateway, String vlanNetmask) { @@ -285,35 +284,35 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen public IPAddressVO markAsUnavailable(long ipAddressId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("id", ipAddressId); - + IPAddressVO ip = createForUpdate(); ip.setState(State.Releasing); if (update(ip, sc) != 1) { return null; } - + return findOneBy(sc); } @Override public long countAllocatedIPsForAccount(long accountId) { - SearchCriteria sc = AllocatedIpCountForAccount.create(); + SearchCriteria sc = AllocatedIpCountForAccount.create(); sc.setParameters("account", accountId); return customSearch(sc, null).get(0); } - + @Override public List listByPhysicalNetworkId(long physicalNetworkId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("physicalNetworkId", physicalNetworkId); return listBy(sc); } - + @Override public long countFreePublicIPs() { - SearchCriteria sc = CountFreePublicIps.create(); - sc.setParameters("state", State.Free); - sc.setJoinParameters("vlans", "vlanType", VlanType.VirtualNetwork); + SearchCriteria sc = CountFreePublicIps.create(); + sc.setParameters("state", State.Free); + sc.setJoinParameters("vlans", "vlanType", VlanType.VirtualNetwork); return customSearch(sc, null).get(0); } @@ -321,21 +320,22 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen public List listByAssociatedVpc(long vpcId, Boolean isSourceNat) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("vpcId", vpcId); - + if (isSourceNat != null) { sc.setParameters("sourceNat", isSourceNat); } - + return listBy(sc); } - + + @Override public long countFreeIPsInNetwork(long networkId) { SearchCriteria sc = CountFreePublicIps.create(); sc.setParameters("state", State.Free); sc.setParameters("networkId", networkId); return customSearch(sc, null).get(0); } - + @Override @DB public boolean remove(Long id) { diff --git a/server/src/com/cloud/network/dao/LoadBalancerDaoImpl.java b/server/src/com/cloud/network/dao/LoadBalancerDaoImpl.java index 31180db56e3..ec9dbc57a71 100644 --- a/server/src/com/cloud/network/dao/LoadBalancerDaoImpl.java +++ b/server/src/com/cloud/network/dao/LoadBalancerDaoImpl.java @@ -29,7 +29,6 @@ import org.springframework.stereotype.Component; import com.cloud.network.LoadBalancerVO; import com.cloud.network.rules.FirewallRule.State; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -135,5 +134,5 @@ public class LoadBalancerDaoImpl extends GenericDaoBase im sc.setParameters("state", State.Add.toString(), State.Revoke.toString()); return listBy(sc); } - + } diff --git a/server/src/com/cloud/network/dao/NetworkDaoImpl.java b/server/src/com/cloud/network/dao/NetworkDaoImpl.java index c6a65dd604f..206373ec8fb 100644 --- a/server/src/com/cloud/network/dao/NetworkDaoImpl.java +++ b/server/src/com/cloud/network/dao/NetworkDaoImpl.java @@ -45,7 +45,6 @@ import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.dao.NetworkOfferingDaoImpl; import com.cloud.server.ResourceTag.TaggedResourceType; import com.cloud.tags.dao.ResourceTagsDaoImpl; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; diff --git a/server/src/com/cloud/network/dao/PhysicalNetworkDaoImpl.java b/server/src/com/cloud/network/dao/PhysicalNetworkDaoImpl.java index e1603dbfa01..f4648fbcde7 100644 --- a/server/src/com/cloud/network/dao/PhysicalNetworkDaoImpl.java +++ b/server/src/com/cloud/network/dao/PhysicalNetworkDaoImpl.java @@ -25,23 +25,20 @@ import org.springframework.stereotype.Component; import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetworkVO; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; -import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.SearchCriteria.Op; @Component @Local(value=PhysicalNetworkDao.class) @DB(txn=false) public class PhysicalNetworkDaoImpl extends GenericDaoBase implements PhysicalNetworkDao { final SearchBuilder ZoneSearch; - + @Inject protected PhysicalNetworkTrafficTypeDaoImpl _trafficTypeDao; - + protected PhysicalNetworkDaoImpl() { super(); ZoneSearch = createSearchBuilder(); @@ -65,7 +62,7 @@ public class PhysicalNetworkDaoImpl extends GenericDaoBase listByZoneAndTrafficType(long dataCenterId, TrafficType trafficType) { - + SearchBuilder trafficTypeSearch = _trafficTypeDao.createSearchBuilder(); PhysicalNetworkTrafficTypeVO trafficTypeEntity = trafficTypeSearch.entity(); trafficTypeSearch.and("trafficType", trafficTypeSearch.entity().getTrafficType(), SearchCriteria.Op.EQ); diff --git a/server/src/com/cloud/network/dao/Site2SiteVpnConnectionDaoImpl.java b/server/src/com/cloud/network/dao/Site2SiteVpnConnectionDaoImpl.java index 2dbae756d0d..8ad61767cae 100644 --- a/server/src/com/cloud/network/dao/Site2SiteVpnConnectionDaoImpl.java +++ b/server/src/com/cloud/network/dao/Site2SiteVpnConnectionDaoImpl.java @@ -25,10 +25,8 @@ import javax.inject.Inject; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; -import com.cloud.network.IPAddressVO; import com.cloud.network.Site2SiteVpnConnectionVO; import com.cloud.network.Site2SiteVpnGatewayVO; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.JoinBuilder.JoinType; import com.cloud.utils.db.SearchBuilder; @@ -41,35 +39,35 @@ public class Site2SiteVpnConnectionDaoImpl extends GenericDaoBase AllFieldsSearch; private SearchBuilder VpcSearch; private SearchBuilder VpnGatewaySearch; public Site2SiteVpnConnectionDaoImpl() { } - + @PostConstruct protected void init() { AllFieldsSearch = createSearchBuilder(); AllFieldsSearch.and("customerGatewayId", AllFieldsSearch.entity().getCustomerGatewayId(), SearchCriteria.Op.EQ); AllFieldsSearch.and("vpnGatewayId", AllFieldsSearch.entity().getVpnGatewayId(), SearchCriteria.Op.EQ); AllFieldsSearch.done(); - + VpcSearch = createSearchBuilder(); VpnGatewaySearch = _vpnGatewayDao.createSearchBuilder(); VpnGatewaySearch.and("vpcId", VpnGatewaySearch.entity().getVpcId(), SearchCriteria.Op.EQ); VpcSearch.join("vpnGatewaySearch", VpnGatewaySearch, VpnGatewaySearch.entity().getId(), VpcSearch.entity().getVpnGatewayId(), JoinType.INNER); VpcSearch.done(); } - + @Override public List listByCustomerGatewayId(long id) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("customerGatewayId", id); return listBy(sc); } - + @Override public List listByVpnGatewayId(long id) { SearchCriteria sc = AllFieldsSearch.create(); diff --git a/server/src/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java b/server/src/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java index b5e0ad5e582..491b172c082 100644 --- a/server/src/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java +++ b/server/src/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java @@ -23,7 +23,6 @@ import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.network.Site2SiteVpnGatewayVO; -import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -32,9 +31,9 @@ import com.cloud.utils.db.SearchCriteria; @Local(value={Site2SiteVpnGatewayDao.class}) public class Site2SiteVpnGatewayDaoImpl extends GenericDaoBase implements Site2SiteVpnGatewayDao { @Inject protected IPAddressDaoImpl _addrDao; - + private static final Logger s_logger = Logger.getLogger(Site2SiteVpnGatewayDaoImpl.class); - + private final SearchBuilder AllFieldsSearch; protected Site2SiteVpnGatewayDaoImpl() { @@ -42,7 +41,7 @@ public class Site2SiteVpnGatewayDaoImpl extends GenericDaoBase sc = AllFieldsSearch.create(); diff --git a/server/src/com/cloud/network/guru/ControlNetworkGuru.java b/server/src/com/cloud/network/guru/ControlNetworkGuru.java index 1195f682819..469a08b31d6 100755 --- a/server/src/com/cloud/network/guru/ControlNetworkGuru.java +++ b/server/src/com/cloud/network/guru/ControlNetworkGuru.java @@ -46,7 +46,7 @@ import com.cloud.network.Networks.Mode; import com.cloud.network.Networks.TrafficType; import com.cloud.offering.NetworkOffering; import com.cloud.user.Account; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.vm.Nic; diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 85e86f6dfd2..6a40f2b6ec5 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -206,7 +206,7 @@ import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.PasswordGenerator; import com.cloud.utils.StringUtils; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; diff --git a/server/src/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java b/server/src/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java index 119ab3d6317..5406ab624e0 100644 --- a/server/src/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java +++ b/server/src/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java @@ -27,7 +27,7 @@ import com.cloud.network.dao.FirewallRulesCidrsDaoImpl; import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.FirewallRule.State; import com.cloud.network.rules.PortForwardingRuleVO; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; diff --git a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java index 0a0844fc3bc..c7848b8f2fa 100755 --- a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java +++ b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java @@ -87,7 +87,7 @@ import com.cloud.uservm.UserVm; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; diff --git a/server/src/com/cloud/network/security/dao/SecurityGroupDaoImpl.java b/server/src/com/cloud/network/security/dao/SecurityGroupDaoImpl.java index bd44328117f..68112c0a7c1 100644 --- a/server/src/com/cloud/network/security/dao/SecurityGroupDaoImpl.java +++ b/server/src/com/cloud/network/security/dao/SecurityGroupDaoImpl.java @@ -26,7 +26,7 @@ import org.springframework.stereotype.Component; import com.cloud.network.security.SecurityGroupVO; import com.cloud.server.ResourceTag.TaggedResourceType; import com.cloud.tags.dao.ResourceTagsDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 58c497e7327..00205a10515 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -99,7 +99,7 @@ import com.cloud.user.UserContext; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; diff --git a/server/src/com/cloud/network/vpc/dao/StaticRouteDaoImpl.java b/server/src/com/cloud/network/vpc/dao/StaticRouteDaoImpl.java index c5270f840ce..0ebccabfa8e 100644 --- a/server/src/com/cloud/network/vpc/dao/StaticRouteDaoImpl.java +++ b/server/src/com/cloud/network/vpc/dao/StaticRouteDaoImpl.java @@ -27,7 +27,7 @@ import com.cloud.network.vpc.StaticRoute; import com.cloud.network.vpc.StaticRouteVO; import com.cloud.server.ResourceTag.TaggedResourceType; import com.cloud.tags.dao.ResourceTagsDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; diff --git a/server/src/com/cloud/network/vpc/dao/VpcDaoImpl.java b/server/src/com/cloud/network/vpc/dao/VpcDaoImpl.java index b7e4d30ba87..a9b5e182b60 100644 --- a/server/src/com/cloud/network/vpc/dao/VpcDaoImpl.java +++ b/server/src/com/cloud/network/vpc/dao/VpcDaoImpl.java @@ -27,7 +27,7 @@ import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.VpcVO; import com.cloud.server.ResourceTag.TaggedResourceType; import com.cloud.tags.dao.ResourceTagsDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; diff --git a/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java index ad75502db42..5b7d9d422cc 100755 --- a/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java +++ b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java @@ -70,7 +70,7 @@ import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.PasswordGenerator; import com.cloud.utils.Ternary; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.Manager; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; diff --git a/server/src/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java b/server/src/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java index d8371c44b17..e34f7d47c85 100644 --- a/server/src/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java +++ b/server/src/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java @@ -70,7 +70,7 @@ import com.cloud.user.dao.AccountDao; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.Manager; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; diff --git a/server/src/com/cloud/projects/dao/ProjectDaoImpl.java b/server/src/com/cloud/projects/dao/ProjectDaoImpl.java index 52ab141f264..e07aecc5ec6 100644 --- a/server/src/com/cloud/projects/dao/ProjectDaoImpl.java +++ b/server/src/com/cloud/projects/dao/ProjectDaoImpl.java @@ -28,7 +28,7 @@ import com.cloud.projects.Project; import com.cloud.projects.ProjectVO; import com.cloud.server.ResourceTag.TaggedResourceType; import com.cloud.tags.dao.ResourceTagsDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; diff --git a/server/src/com/cloud/resource/DiscovererBase.java b/server/src/com/cloud/resource/DiscovererBase.java index 6f6d1bace00..8bf599bdf06 100644 --- a/server/src/com/cloud/resource/DiscovererBase.java +++ b/server/src/com/cloud/resource/DiscovererBase.java @@ -34,7 +34,7 @@ import com.cloud.dc.dao.ClusterDao; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.network.NetworkManager; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.net.UrlUtil; public abstract class DiscovererBase implements Discoverer { diff --git a/server/src/com/cloud/servlet/ConsoleProxyServlet.java b/server/src/com/cloud/servlet/ConsoleProxyServlet.java index afa5c407316..c1001da20d6 100644 --- a/server/src/com/cloud/servlet/ConsoleProxyServlet.java +++ b/server/src/com/cloud/servlet/ConsoleProxyServlet.java @@ -46,7 +46,7 @@ import com.cloud.user.User; import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.Transaction; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; diff --git a/server/src/com/cloud/servlet/RegisterCompleteServlet.java b/server/src/com/cloud/servlet/RegisterCompleteServlet.java index 7755851a2d8..d159c887fab 100644 --- a/server/src/com/cloud/servlet/RegisterCompleteServlet.java +++ b/server/src/com/cloud/servlet/RegisterCompleteServlet.java @@ -37,7 +37,7 @@ import com.cloud.user.User; import com.cloud.user.UserVO; import com.cloud.user.dao.UserDao; import com.cloud.utils.SerialVersionUID; -import com.cloud.utils.component.ComponentLocator; + public class RegisterCompleteServlet extends HttpServlet implements ServletContextListener { public static final Logger s_logger = Logger.getLogger(RegisterCompleteServlet.class.getName()); diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index ef36a829ccb..602d10d19c3 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -173,7 +173,7 @@ import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.UriUtils; import com.cloud.utils.component.Adapters; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; diff --git a/server/src/com/cloud/storage/StorageMigrationCleanupMaid.java b/server/src/com/cloud/storage/StorageMigrationCleanupMaid.java index 3ba8483d320..03a49760f98 100644 --- a/server/src/com/cloud/storage/StorageMigrationCleanupMaid.java +++ b/server/src/com/cloud/storage/StorageMigrationCleanupMaid.java @@ -25,7 +25,7 @@ import com.cloud.cluster.CheckPointManager; import com.cloud.cluster.CleanupMaid; import com.cloud.server.ManagementServer; import com.cloud.storage.dao.VolumeDao; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.Transaction; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.StateMachine2; diff --git a/server/src/com/cloud/storage/allocator/GarbageCollectingStoragePoolAllocator.java b/server/src/com/cloud/storage/allocator/GarbageCollectingStoragePoolAllocator.java index 3e1030d077c..dbef8d5f250 100644 --- a/server/src/com/cloud/storage/allocator/GarbageCollectingStoragePoolAllocator.java +++ b/server/src/com/cloud/storage/allocator/GarbageCollectingStoragePoolAllocator.java @@ -31,7 +31,7 @@ import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.vm.DiskProfile; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; diff --git a/server/src/com/cloud/storage/allocator/UseLocalForRootAllocator.java b/server/src/com/cloud/storage/allocator/UseLocalForRootAllocator.java index ace7303170c..2c19406fef6 100644 --- a/server/src/com/cloud/storage/allocator/UseLocalForRootAllocator.java +++ b/server/src/com/cloud/storage/allocator/UseLocalForRootAllocator.java @@ -34,7 +34,7 @@ import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.host.Host; import com.cloud.storage.StoragePool; import com.cloud.storage.Volume.Type; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.vm.DiskProfile; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; diff --git a/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java b/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java index 7868b3619d3..8347c4e1301 100644 --- a/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java +++ b/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java @@ -34,7 +34,7 @@ import com.cloud.storage.SnapshotVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; import com.cloud.tags.dao.ResourceTagsDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; diff --git a/server/src/com/cloud/storage/dao/StoragePoolDaoImpl.java b/server/src/com/cloud/storage/dao/StoragePoolDaoImpl.java index 9a50189277d..4019dffd4ae 100644 --- a/server/src/com/cloud/storage/dao/StoragePoolDaoImpl.java +++ b/server/src/com/cloud/storage/dao/StoragePoolDaoImpl.java @@ -35,7 +35,7 @@ import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StoragePoolDetailVO; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StoragePoolVO; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; diff --git a/server/src/com/cloud/storage/dao/VMTemplateDaoImpl.java b/server/src/com/cloud/storage/dao/VMTemplateDaoImpl.java index 8093fd5f9e2..42f10d34c1b 100755 --- a/server/src/com/cloud/storage/dao/VMTemplateDaoImpl.java +++ b/server/src/com/cloud/storage/dao/VMTemplateDaoImpl.java @@ -59,7 +59,7 @@ import com.cloud.tags.dao.ResourceTagsDaoImpl; import com.cloud.template.VirtualMachineTemplate.TemplateFilter; import com.cloud.user.Account; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; diff --git a/server/src/com/cloud/storage/dao/VolumeDaoImpl.java b/server/src/com/cloud/storage/dao/VolumeDaoImpl.java index d4cc692c06a..a189d00fead 100755 --- a/server/src/com/cloud/storage/dao/VolumeDaoImpl.java +++ b/server/src/com/cloud/storage/dao/VolumeDaoImpl.java @@ -39,7 +39,7 @@ import com.cloud.storage.Volume.Type; import com.cloud.storage.VolumeVO; import com.cloud.tags.dao.ResourceTagsDaoImpl; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; diff --git a/server/src/com/cloud/storage/listener/StoragePoolMonitor.java b/server/src/com/cloud/storage/listener/StoragePoolMonitor.java index af6f0f5276a..3a7dac7a70f 100755 --- a/server/src/com/cloud/storage/listener/StoragePoolMonitor.java +++ b/server/src/com/cloud/storage/listener/StoragePoolMonitor.java @@ -38,7 +38,7 @@ import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StoragePoolVO; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.dao.StoragePoolDao; -import com.cloud.utils.component.ComponentLocator; + public class StoragePoolMonitor implements Listener { private static final Logger s_logger = Logger.getLogger(StoragePoolMonitor.class); diff --git a/server/src/com/cloud/storage/resource/DummySecondaryStorageResource.java b/server/src/com/cloud/storage/resource/DummySecondaryStorageResource.java index 23f3def0c22..1f33330811b 100644 --- a/server/src/com/cloud/storage/resource/DummySecondaryStorageResource.java +++ b/server/src/com/cloud/storage/resource/DummySecondaryStorageResource.java @@ -50,7 +50,7 @@ import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.template.TemplateConstants; import com.cloud.storage.template.TemplateInfo; -import com.cloud.utils.component.ComponentLocator; + public class DummySecondaryStorageResource extends ServerResourceBase implements ServerResource { private static final Logger s_logger = Logger.getLogger(DummySecondaryStorageResource.class); diff --git a/server/src/com/cloud/storage/s3/S3ManagerImpl.java b/server/src/com/cloud/storage/s3/S3ManagerImpl.java index 069edf37612..8a1354c9282 100644 --- a/server/src/com/cloud/storage/s3/S3ManagerImpl.java +++ b/server/src/com/cloud/storage/s3/S3ManagerImpl.java @@ -77,7 +77,7 @@ import com.cloud.storage.dao.VMTemplateS3Dao; import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.utils.S3Utils.ClientOptions; -import com.cloud.utils.component.Inject; + import com.cloud.utils.db.Filter; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.exception.CloudRuntimeException; diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index fb4ece5187c..6bf6d5e5f2a 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -114,7 +114,7 @@ import com.cloud.utils.DateUtil.IntervalType; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.Manager; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; diff --git a/server/src/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java index 190b054a1d0..18dc1f9d053 100644 --- a/server/src/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java @@ -55,7 +55,7 @@ import com.cloud.user.User; import com.cloud.utils.DateUtil; import com.cloud.utils.DateUtil.IntervalType; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.concurrency.TestClock; import com.cloud.utils.db.DB; import com.cloud.utils.db.GlobalLock; diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index 0c208530b54..3a8bec5ff87 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -131,7 +131,7 @@ import com.cloud.user.dao.UserDao; import com.cloud.uservm.UserVm; import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.AdapterBase; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; diff --git a/server/src/com/cloud/test/IPRangeConfig.java b/server/src/com/cloud/test/IPRangeConfig.java index c8bc76c3163..1f1f90f006a 100755 --- a/server/src/com/cloud/test/IPRangeConfig.java +++ b/server/src/com/cloud/test/IPRangeConfig.java @@ -26,7 +26,7 @@ import java.util.List; import java.util.UUID; import java.util.Vector; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; import com.cloud.utils.net.NetUtils; diff --git a/server/src/com/cloud/test/PodZoneConfig.java b/server/src/com/cloud/test/PodZoneConfig.java index f5787211e57..70ee0acc8b5 100644 --- a/server/src/com/cloud/test/PodZoneConfig.java +++ b/server/src/com/cloud/test/PodZoneConfig.java @@ -25,7 +25,7 @@ import java.util.List; import java.util.Vector; import com.cloud.network.Networks.TrafficType; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; import com.cloud.utils.net.NetUtils; diff --git a/server/src/com/cloud/upgrade/DatabaseCreator.java b/server/src/com/cloud/upgrade/DatabaseCreator.java index 079e1e93b14..f0a8c5a03b2 100755 --- a/server/src/com/cloud/upgrade/DatabaseCreator.java +++ b/server/src/com/cloud/upgrade/DatabaseCreator.java @@ -26,7 +26,7 @@ import java.sql.Connection; import java.sql.SQLException; import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.SystemIntegrityChecker; import com.cloud.utils.db.ScriptRunner; import com.cloud.utils.db.Transaction; diff --git a/server/src/com/cloud/upgrade/DatabaseIntegrityChecker.java b/server/src/com/cloud/upgrade/DatabaseIntegrityChecker.java index d6e55a9d0af..1905bb3c2f8 100755 --- a/server/src/com/cloud/upgrade/DatabaseIntegrityChecker.java +++ b/server/src/com/cloud/upgrade/DatabaseIntegrityChecker.java @@ -30,7 +30,7 @@ import org.springframework.stereotype.Component; import com.cloud.maint.Version; import com.cloud.upgrade.dao.VersionDao; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.SystemIntegrityChecker; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.Transaction; diff --git a/server/src/com/cloud/upgrade/DatabaseUpgradeChecker.java b/server/src/com/cloud/upgrade/DatabaseUpgradeChecker.java index c07d03f873e..f831a032385 100755 --- a/server/src/com/cloud/upgrade/DatabaseUpgradeChecker.java +++ b/server/src/com/cloud/upgrade/DatabaseUpgradeChecker.java @@ -63,7 +63,7 @@ import com.cloud.upgrade.dao.VersionDao; import com.cloud.upgrade.dao.VersionDaoImpl; import com.cloud.upgrade.dao.VersionVO; import com.cloud.upgrade.dao.VersionVO.Step; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.SystemIntegrityChecker; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.ScriptRunner; diff --git a/server/src/com/cloud/upgrade/PremiumDatabaseUpgradeChecker.java b/server/src/com/cloud/upgrade/PremiumDatabaseUpgradeChecker.java index 43d025ada22..896cb5618ed 100755 --- a/server/src/com/cloud/upgrade/PremiumDatabaseUpgradeChecker.java +++ b/server/src/com/cloud/upgrade/PremiumDatabaseUpgradeChecker.java @@ -43,7 +43,7 @@ import com.cloud.upgrade.dao.Upgrade30to301; import com.cloud.upgrade.dao.UpgradeSnapshot217to224; import com.cloud.upgrade.dao.UpgradeSnapshot223to224; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.SystemIntegrityChecker; @Component diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index b90413218de..dc0370bc94b 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -113,7 +113,7 @@ import com.cloud.user.dao.UserDao; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 55d0d4fe756..197230475b8 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -214,7 +214,7 @@ import com.cloud.uservm.UserVm; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.PasswordGenerator; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.crypt.RSAHelper; diff --git a/server/src/com/cloud/vm/dao/UserVmDaoImpl.java b/server/src/com/cloud/vm/dao/UserVmDaoImpl.java index 8eda2e24ba1..e2cf02e010c 100755 --- a/server/src/com/cloud/vm/dao/UserVmDaoImpl.java +++ b/server/src/com/cloud/vm/dao/UserVmDaoImpl.java @@ -37,7 +37,7 @@ import com.cloud.configuration.Resource; import com.cloud.server.ResourceTag.TaggedResourceType; import com.cloud.tags.dao.ResourceTagsDaoImpl; import com.cloud.user.Account; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.Attribute; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; diff --git a/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java b/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java index d5693da9d0c..2e1d8514f7f 100644 --- a/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/server/src/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -39,7 +39,7 @@ import com.cloud.host.dao.HostDaoImpl; import com.cloud.server.ResourceTag.TaggedResourceType; import com.cloud.tags.dao.ResourceTagsDaoImpl; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.Attribute; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; diff --git a/server/test/com/cloud/network/security/SecurityGroupManagerImpl2Test.java b/server/test/com/cloud/network/security/SecurityGroupManagerImpl2Test.java index 461bde0b95d..6bf94a2ef95 100644 --- a/server/test/com/cloud/network/security/SecurityGroupManagerImpl2Test.java +++ b/server/test/com/cloud/network/security/SecurityGroupManagerImpl2Test.java @@ -46,7 +46,7 @@ import com.cloud.user.MockAccountManagerImpl; import com.cloud.user.MockDomainManagerImpl; import com.cloud.user.dao.AccountDaoImpl; import com.cloud.utils.Profiler; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.MockComponentLocator; import com.cloud.vm.MockUserVmManagerImpl; import com.cloud.vm.MockVirtualMachineManagerImpl; diff --git a/server/test/com/cloud/snapshot/SnapshotDaoTest.java b/server/test/com/cloud/snapshot/SnapshotDaoTest.java index c412f49b3d4..644980b6931 100644 --- a/server/test/com/cloud/snapshot/SnapshotDaoTest.java +++ b/server/test/com/cloud/snapshot/SnapshotDaoTest.java @@ -21,7 +21,7 @@ import java.util.List; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; import com.cloud.storage.dao.SnapshotDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import junit.framework.Assert; import junit.framework.TestCase; diff --git a/server/test/com/cloud/storage/dao/StoragePoolDaoTest.java b/server/test/com/cloud/storage/dao/StoragePoolDaoTest.java index ed766f557f6..57d74f682f9 100644 --- a/server/test/com/cloud/storage/dao/StoragePoolDaoTest.java +++ b/server/test/com/cloud/storage/dao/StoragePoolDaoTest.java @@ -19,7 +19,7 @@ package com.cloud.storage.dao; import junit.framework.TestCase; import com.cloud.storage.StoragePoolStatus; -import com.cloud.utils.component.ComponentLocator; + public class StoragePoolDaoTest extends TestCase { diff --git a/server/test/com/cloud/upgrade/AdvanceZone217To224UpgradeTest.java b/server/test/com/cloud/upgrade/AdvanceZone217To224UpgradeTest.java index 27b2a7b723c..9d442dcf4fa 100644 --- a/server/test/com/cloud/upgrade/AdvanceZone217To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/AdvanceZone217To224UpgradeTest.java @@ -29,7 +29,7 @@ import org.junit.After; import org.junit.Before; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DbTestUtils; import com.cloud.utils.db.Transaction; diff --git a/server/test/com/cloud/upgrade/AdvanceZone223To224UpgradeTest.java b/server/test/com/cloud/upgrade/AdvanceZone223To224UpgradeTest.java index a0394993404..17f3158d324 100644 --- a/server/test/com/cloud/upgrade/AdvanceZone223To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/AdvanceZone223To224UpgradeTest.java @@ -25,7 +25,7 @@ import org.junit.After; import org.junit.Before; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + public class AdvanceZone223To224UpgradeTest extends TestCase { private static final Logger s_logger = Logger.getLogger(AdvanceZone223To224UpgradeTest.class); diff --git a/server/test/com/cloud/upgrade/BasicZone218To224UpgradeTest.java b/server/test/com/cloud/upgrade/BasicZone218To224UpgradeTest.java index 521e92a042f..19048e0c923 100644 --- a/server/test/com/cloud/upgrade/BasicZone218To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/BasicZone218To224UpgradeTest.java @@ -29,7 +29,7 @@ import org.junit.After; import org.junit.Before; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DbTestUtils; import com.cloud.utils.db.Transaction; diff --git a/server/test/com/cloud/upgrade/HostCapacity218to22Test.java b/server/test/com/cloud/upgrade/HostCapacity218to22Test.java index af6321abf60..3d89f219520 100644 --- a/server/test/com/cloud/upgrade/HostCapacity218to22Test.java +++ b/server/test/com/cloud/upgrade/HostCapacity218to22Test.java @@ -25,7 +25,7 @@ import org.junit.After; import org.junit.Before; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DbTestUtils; public class HostCapacity218to22Test extends TestCase { diff --git a/server/test/com/cloud/upgrade/InstanceGroup218To224UpgradeTest.java b/server/test/com/cloud/upgrade/InstanceGroup218To224UpgradeTest.java index 601b1e83212..8624019e0c7 100644 --- a/server/test/com/cloud/upgrade/InstanceGroup218To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/InstanceGroup218To224UpgradeTest.java @@ -30,7 +30,7 @@ import org.junit.After; import org.junit.Before; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DbTestUtils; import com.cloud.utils.db.Transaction; diff --git a/server/test/com/cloud/upgrade/PortForwarding218To224UpgradeTest.java b/server/test/com/cloud/upgrade/PortForwarding218To224UpgradeTest.java index a430584d7bf..647d2b07dfc 100644 --- a/server/test/com/cloud/upgrade/PortForwarding218To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/PortForwarding218To224UpgradeTest.java @@ -29,7 +29,7 @@ import org.junit.After; import org.junit.Before; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DbTestUtils; import com.cloud.utils.db.Transaction; diff --git a/server/test/com/cloud/upgrade/Sanity220To224UpgradeTest.java b/server/test/com/cloud/upgrade/Sanity220To224UpgradeTest.java index ef47aadc83b..080482fa6f8 100644 --- a/server/test/com/cloud/upgrade/Sanity220To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/Sanity220To224UpgradeTest.java @@ -28,7 +28,7 @@ import org.junit.After; import org.junit.Before; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DbTestUtils; import com.cloud.utils.db.Transaction; diff --git a/server/test/com/cloud/upgrade/Sanity222To224UpgradeTest.java b/server/test/com/cloud/upgrade/Sanity222To224UpgradeTest.java index aa30df2a5f6..44e90eb05c8 100644 --- a/server/test/com/cloud/upgrade/Sanity222To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/Sanity222To224UpgradeTest.java @@ -28,7 +28,7 @@ import org.junit.After; import org.junit.Before; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DbTestUtils; import com.cloud.utils.db.Transaction; diff --git a/server/test/com/cloud/upgrade/Sanity223To225UpgradeTest.java b/server/test/com/cloud/upgrade/Sanity223To225UpgradeTest.java index 32910276948..7312cc69894 100644 --- a/server/test/com/cloud/upgrade/Sanity223To225UpgradeTest.java +++ b/server/test/com/cloud/upgrade/Sanity223To225UpgradeTest.java @@ -28,7 +28,7 @@ import org.junit.After; import org.junit.Before; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.Transaction; public class Sanity223To225UpgradeTest extends TestCase { diff --git a/server/test/com/cloud/upgrade/Sanity224To225UpgradeTest.java b/server/test/com/cloud/upgrade/Sanity224To225UpgradeTest.java index a7b6ba152a4..89ab58b4911 100644 --- a/server/test/com/cloud/upgrade/Sanity224To225UpgradeTest.java +++ b/server/test/com/cloud/upgrade/Sanity224To225UpgradeTest.java @@ -28,7 +28,7 @@ import org.junit.After; import org.junit.Before; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DbTestUtils; import com.cloud.utils.db.Transaction; diff --git a/server/test/com/cloud/upgrade/Template2214To30UpgradeTest.java b/server/test/com/cloud/upgrade/Template2214To30UpgradeTest.java index e7a01e30859..88a4ea4dcd1 100644 --- a/server/test/com/cloud/upgrade/Template2214To30UpgradeTest.java +++ b/server/test/com/cloud/upgrade/Template2214To30UpgradeTest.java @@ -29,7 +29,7 @@ import org.apache.log4j.Logger; import org.junit.After; import org.junit.Before; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DbTestUtils; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; diff --git a/server/test/com/cloud/upgrade/Test2214To30DBUpgrade.java b/server/test/com/cloud/upgrade/Test2214To30DBUpgrade.java index 5f05ac32a1c..66a2fda7c20 100644 --- a/server/test/com/cloud/upgrade/Test2214To30DBUpgrade.java +++ b/server/test/com/cloud/upgrade/Test2214To30DBUpgrade.java @@ -29,7 +29,7 @@ import org.apache.log4j.Logger; import org.junit.After; import org.junit.Before; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DbTestUtils; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; diff --git a/server/test/com/cloud/upgrade/Usage217To224UpgradeTest.java b/server/test/com/cloud/upgrade/Usage217To224UpgradeTest.java index d349247a810..17a85fb6399 100644 --- a/server/test/com/cloud/upgrade/Usage217To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/Usage217To224UpgradeTest.java @@ -29,7 +29,7 @@ import org.junit.After; import org.junit.Before; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DbTestUtils; import com.cloud.utils.db.Transaction; diff --git a/server/test/com/cloud/upgrade/UsageEvents218To224UpgradeTest.java b/server/test/com/cloud/upgrade/UsageEvents218To224UpgradeTest.java index 7319afa4469..94ae83628da 100644 --- a/server/test/com/cloud/upgrade/UsageEvents218To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/UsageEvents218To224UpgradeTest.java @@ -29,7 +29,7 @@ import org.junit.After; import org.junit.Before; import com.cloud.upgrade.dao.VersionDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DbTestUtils; import com.cloud.utils.db.Transaction; diff --git a/server/test/com/cloud/vm/dao/UserVmDaoImplTest.java b/server/test/com/cloud/vm/dao/UserVmDaoImplTest.java index f07abca439c..b062dd2a414 100644 --- a/server/test/com/cloud/vm/dao/UserVmDaoImplTest.java +++ b/server/test/com/cloud/vm/dao/UserVmDaoImplTest.java @@ -19,7 +19,7 @@ package com.cloud.vm.dao; import junit.framework.TestCase; import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; diff --git a/server/test/com/cloud/vpc/MockVpcManagerImpl.java b/server/test/com/cloud/vpc/MockVpcManagerImpl.java index 25799d19b9e..c41bb7b27e8 100644 --- a/server/test/com/cloud/vpc/MockVpcManagerImpl.java +++ b/server/test/com/cloud/vpc/MockVpcManagerImpl.java @@ -50,7 +50,7 @@ import com.cloud.offering.NetworkOffering; import com.cloud.user.Account; import com.cloud.user.User; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.Manager; import com.cloud.vm.DomainRouterVO; import com.cloud.vpc.dao.MockVpcDaoImpl; diff --git a/server/test/com/cloud/vpc/VpcApiUnitTest.java b/server/test/com/cloud/vpc/VpcApiUnitTest.java index 5cc325ffac0..14e376a9ee8 100644 --- a/server/test/com/cloud/vpc/VpcApiUnitTest.java +++ b/server/test/com/cloud/vpc/VpcApiUnitTest.java @@ -45,7 +45,7 @@ import com.cloud.tags.dao.ResourceTagsDaoImpl; import com.cloud.user.AccountVO; import com.cloud.user.MockAccountManagerImpl; import com.cloud.user.dao.AccountDaoImpl; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.component.MockComponentLocator; import com.cloud.vm.dao.DomainRouterDaoImpl; import com.cloud.vpc.dao.MockNetworkDaoImpl; diff --git a/usage/src/com/cloud/usage/UsageAlertManagerImpl.java b/usage/src/com/cloud/usage/UsageAlertManagerImpl.java index 2b698c83708..f6e0374875a 100644 --- a/usage/src/com/cloud/usage/UsageAlertManagerImpl.java +++ b/usage/src/com/cloud/usage/UsageAlertManagerImpl.java @@ -40,7 +40,7 @@ import com.cloud.alert.AlertVO; import com.cloud.alert.dao.AlertDao; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.component.ComponentLocator; + import com.sun.mail.smtp.SMTPMessage; import com.sun.mail.smtp.SMTPSSLTransport; import com.sun.mail.smtp.SMTPTransport; diff --git a/usage/src/com/cloud/usage/UsageManagerImpl.java b/usage/src/com/cloud/usage/UsageManagerImpl.java index bbf2cd65ba8..971f5dfa707 100644 --- a/usage/src/com/cloud/usage/UsageManagerImpl.java +++ b/usage/src/com/cloud/usage/UsageManagerImpl.java @@ -66,8 +66,8 @@ import com.cloud.user.AccountVO; import com.cloud.user.UserStatisticsVO; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserStatisticsDao; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.component.Inject; + + import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; diff --git a/usage/src/com/cloud/usage/UsageServer.java b/usage/src/com/cloud/usage/UsageServer.java index 4cdca7986c2..aedbe768476 100644 --- a/usage/src/com/cloud/usage/UsageServer.java +++ b/usage/src/com/cloud/usage/UsageServer.java @@ -18,7 +18,7 @@ package com.cloud.usage; import org.apache.log4j.Logger; -import com.cloud.utils.component.ComponentLocator; + public class UsageServer { private static final Logger s_logger = Logger.getLogger(UsageServer.class.getName()); diff --git a/usage/src/com/cloud/usage/parser/IPAddressUsageParser.java b/usage/src/com/cloud/usage/parser/IPAddressUsageParser.java index 08cb02190e6..f60c0bf6b40 100644 --- a/usage/src/com/cloud/usage/parser/IPAddressUsageParser.java +++ b/usage/src/com/cloud/usage/parser/IPAddressUsageParser.java @@ -32,7 +32,7 @@ import com.cloud.usage.dao.UsageDao; import com.cloud.usage.dao.UsageIPAddressDao; import com.cloud.user.AccountVO; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + public class IPAddressUsageParser { public static final Logger s_logger = Logger.getLogger(IPAddressUsageParser.class.getName()); diff --git a/usage/src/com/cloud/usage/parser/LoadBalancerUsageParser.java b/usage/src/com/cloud/usage/parser/LoadBalancerUsageParser.java index c1423c6cc8d..8763527b398 100644 --- a/usage/src/com/cloud/usage/parser/LoadBalancerUsageParser.java +++ b/usage/src/com/cloud/usage/parser/LoadBalancerUsageParser.java @@ -32,7 +32,7 @@ import com.cloud.usage.dao.UsageDao; import com.cloud.usage.dao.UsageLoadBalancerPolicyDao; import com.cloud.user.AccountVO; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + public class LoadBalancerUsageParser { public static final Logger s_logger = Logger.getLogger(LoadBalancerUsageParser.class.getName()); diff --git a/usage/src/com/cloud/usage/parser/NetworkOfferingUsageParser.java b/usage/src/com/cloud/usage/parser/NetworkOfferingUsageParser.java index fc7fc2a54e7..c2bf1c30427 100644 --- a/usage/src/com/cloud/usage/parser/NetworkOfferingUsageParser.java +++ b/usage/src/com/cloud/usage/parser/NetworkOfferingUsageParser.java @@ -32,7 +32,7 @@ import com.cloud.usage.dao.UsageDao; import com.cloud.usage.dao.UsageNetworkOfferingDao; import com.cloud.user.AccountVO; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + public class NetworkOfferingUsageParser { public static final Logger s_logger = Logger.getLogger(NetworkOfferingUsageParser.class.getName()); diff --git a/usage/src/com/cloud/usage/parser/NetworkUsageParser.java b/usage/src/com/cloud/usage/parser/NetworkUsageParser.java index acdbc484dcd..c0ba15f08c4 100644 --- a/usage/src/com/cloud/usage/parser/NetworkUsageParser.java +++ b/usage/src/com/cloud/usage/parser/NetworkUsageParser.java @@ -31,7 +31,7 @@ import com.cloud.usage.dao.UsageDao; import com.cloud.usage.dao.UsageNetworkDao; import com.cloud.user.AccountVO; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.SearchCriteria; public class NetworkUsageParser { diff --git a/usage/src/com/cloud/usage/parser/PortForwardingUsageParser.java b/usage/src/com/cloud/usage/parser/PortForwardingUsageParser.java index 469fb655f9e..42cc74c10fb 100644 --- a/usage/src/com/cloud/usage/parser/PortForwardingUsageParser.java +++ b/usage/src/com/cloud/usage/parser/PortForwardingUsageParser.java @@ -32,7 +32,7 @@ import com.cloud.usage.dao.UsageDao; import com.cloud.usage.dao.UsagePortForwardingRuleDao; import com.cloud.user.AccountVO; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + public class PortForwardingUsageParser { public static final Logger s_logger = Logger.getLogger(PortForwardingUsageParser.class.getName()); diff --git a/usage/src/com/cloud/usage/parser/SecurityGroupUsageParser.java b/usage/src/com/cloud/usage/parser/SecurityGroupUsageParser.java index 28323851ed9..f175ae27d19 100644 --- a/usage/src/com/cloud/usage/parser/SecurityGroupUsageParser.java +++ b/usage/src/com/cloud/usage/parser/SecurityGroupUsageParser.java @@ -32,7 +32,7 @@ import com.cloud.usage.dao.UsageDao; import com.cloud.usage.dao.UsageSecurityGroupDao; import com.cloud.user.AccountVO; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + public class SecurityGroupUsageParser { public static final Logger s_logger = Logger.getLogger(SecurityGroupUsageParser.class.getName()); diff --git a/usage/src/com/cloud/usage/parser/StorageUsageParser.java b/usage/src/com/cloud/usage/parser/StorageUsageParser.java index 4d48e39b750..a0fee2df535 100644 --- a/usage/src/com/cloud/usage/parser/StorageUsageParser.java +++ b/usage/src/com/cloud/usage/parser/StorageUsageParser.java @@ -33,7 +33,7 @@ import com.cloud.usage.dao.UsageDao; import com.cloud.usage.dao.UsageStorageDao; import com.cloud.user.AccountVO; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + public class StorageUsageParser { public static final Logger s_logger = Logger.getLogger(StorageUsageParser.class.getName()); diff --git a/usage/src/com/cloud/usage/parser/VMInstanceUsageParser.java b/usage/src/com/cloud/usage/parser/VMInstanceUsageParser.java index 681e8ec31ba..0680ec3afb9 100644 --- a/usage/src/com/cloud/usage/parser/VMInstanceUsageParser.java +++ b/usage/src/com/cloud/usage/parser/VMInstanceUsageParser.java @@ -32,7 +32,7 @@ import com.cloud.usage.dao.UsageDao; import com.cloud.usage.dao.UsageVMInstanceDao; import com.cloud.user.AccountVO; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + public class VMInstanceUsageParser { public static final Logger s_logger = Logger.getLogger(VMInstanceUsageParser.class.getName()); diff --git a/usage/src/com/cloud/usage/parser/VPNUserUsageParser.java b/usage/src/com/cloud/usage/parser/VPNUserUsageParser.java index 089bf9072c0..7a96e3ce7b0 100644 --- a/usage/src/com/cloud/usage/parser/VPNUserUsageParser.java +++ b/usage/src/com/cloud/usage/parser/VPNUserUsageParser.java @@ -32,7 +32,7 @@ import com.cloud.usage.dao.UsageDao; import com.cloud.usage.dao.UsageVPNUserDao; import com.cloud.user.AccountVO; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + public class VPNUserUsageParser { public static final Logger s_logger = Logger.getLogger(VPNUserUsageParser.class.getName()); diff --git a/usage/src/com/cloud/usage/parser/VolumeUsageParser.java b/usage/src/com/cloud/usage/parser/VolumeUsageParser.java index db58f41a6ef..1eb7664c290 100644 --- a/usage/src/com/cloud/usage/parser/VolumeUsageParser.java +++ b/usage/src/com/cloud/usage/parser/VolumeUsageParser.java @@ -32,7 +32,7 @@ import com.cloud.usage.dao.UsageDao; import com.cloud.usage.dao.UsageVolumeDao; import com.cloud.user.AccountVO; import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; + public class VolumeUsageParser { public static final Logger s_logger = Logger.getLogger(VolumeUsageParser.class.getName()); diff --git a/utils/test/com/cloud/utils/db/TransactionTest.java b/utils/test/com/cloud/utils/db/TransactionTest.java index 96d31b40597..5bd1e9aca65 100644 --- a/utils/test/com/cloud/utils/db/TransactionTest.java +++ b/utils/test/com/cloud/utils/db/TransactionTest.java @@ -26,7 +26,7 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.exception.CloudRuntimeException; /** diff --git a/utils/test/com/cloud/utils/log/CglibThrowableRendererTest.java b/utils/test/com/cloud/utils/log/CglibThrowableRendererTest.java index 5a9501dcc9c..13e396db06d 100644 --- a/utils/test/com/cloud/utils/log/CglibThrowableRendererTest.java +++ b/utils/test/com/cloud/utils/log/CglibThrowableRendererTest.java @@ -20,7 +20,7 @@ import junit.framework.TestCase; import org.apache.log4j.Logger; -import com.cloud.utils.component.ComponentLocator; + import com.cloud.utils.db.DB; import com.cloud.utils.exception.CloudRuntimeException; From 62a42723f995279fcaa4a63d9b0be061d32c66ca Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 11:49:15 -0800 Subject: [PATCH 19/73] APIAccessChecker: Make it check based on role type and not user Signed-off-by: Rohit Yadav --- .../cloudstack/acl/APIAccessChecker.java | 7 +- .../acl/StaticRoleBasedAPIAccessChecker.java | 84 ++++++++----------- server/src/com/cloud/api/ApiServer.java | 33 +++++++- 3 files changed, 69 insertions(+), 55 deletions(-) diff --git a/api/src/org/apache/cloudstack/acl/APIAccessChecker.java b/api/src/org/apache/cloudstack/acl/APIAccessChecker.java index 3194bd11d17..a5c656d731a 100644 --- a/api/src/org/apache/cloudstack/acl/APIAccessChecker.java +++ b/api/src/org/apache/cloudstack/acl/APIAccessChecker.java @@ -16,11 +16,8 @@ // under the License. package org.apache.cloudstack.acl; -import java.util.Properties; - +import org.apache.cloudstack.acl.RoleType; import com.cloud.exception.PermissionDeniedException; -import com.cloud.user.Account; -import com.cloud.user.User; import com.cloud.utils.component.Adapter; /** @@ -28,5 +25,5 @@ import com.cloud.utils.component.Adapter; */ public interface APIAccessChecker extends Adapter { // Interface for checking access to an API for an user - boolean canAccessAPI(User user, String apiCommandName) throws PermissionDeniedException; + boolean canAccessAPI(RoleType roleType, String apiCommandName) throws PermissionDeniedException; } 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..43ca403f890 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 @@ -27,80 +27,66 @@ import javax.ejb.Local; import javax.naming.ConfigurationException; import org.apache.cloudstack.acl.APIAccessChecker; +import org.apache.cloudstack.acl.RoleType; +import static org.apache.cloudstack.acl.RoleType.*; 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. - */ - +// This is the default API access checker that grab's the user's account +// based on the account type, access is granted @Local(value=APIAccessChecker.class) public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIAccessChecker { 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 Set s_userCommands = null; + private static Set s_resellerCommands = null; // AKA domain-admin + private static Set s_adminCommands = null; + private static Set s_resourceDomainAdminCommands = null; + private static Set s_allCommands = null; protected StaticRoleBasedAPIAccessChecker() { super(); - s_allCommands = new ArrayList(); - s_userCommands = new ArrayList(); - s_resellerCommands = new ArrayList(); - s_adminCommands = new ArrayList(); - s_resourceDomainAdminCommands = new ArrayList(); + s_allCommands = new HashSet(); + s_userCommands = new HashSet(); + s_resellerCommands = new HashSet(); + s_adminCommands = new HashSet(); + s_resourceDomainAdminCommands = new HashSet(); } @Override - public boolean canAccessAPI(User user, String apiCommandName) + public boolean canAccessAPI(RoleType roleType, 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); + if(commandExists) { + return isCommandAvailableForAccount(roleType, apiCommandName); } return commandExists; } - private static boolean isCommandAvailableForAccount(short accountType, String commandName) { + private static boolean isCommandAvailableForAccount(RoleType roleType, 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; + switch (roleType) { + case Admin: + isCommandAvailable = s_adminCommands.contains(commandName); + break; + case DomainAdmin: + isCommandAvailable = s_resellerCommands.contains(commandName); + break; + case ResourceAdmin: + isCommandAvailable = s_resourceDomainAdminCommands.contains(commandName); + break; + case User: + isCommandAvailable = s_userCommands.contains(commandName); + break; } return isCommandAvailable; } @@ -157,16 +143,16 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIA try { short cmdPermissions = Short.parseShort(mask); - if ((cmdPermissions & ADMIN_COMMAND) != 0) { + if ((cmdPermissions & Admin.getValue()) != 0) { s_adminCommands.add((String) key); } - if ((cmdPermissions & RESOURCE_DOMAIN_ADMIN_COMMAND) != 0) { + if ((cmdPermissions & ResourceAdmin.getValue()) != 0) { s_resourceDomainAdminCommands.add((String) key); } - if ((cmdPermissions & DOMAIN_ADMIN_COMMAND) != 0) { + if ((cmdPermissions & DomainAdmin.getValue()) != 0) { s_resellerCommands.add((String) key); } - if ((cmdPermissions & USER_COMMAND) != 0) { + if ((cmdPermissions & User.getValue()) != 0) { s_userCommands.add((String) key); } s_allCommands.addAll(s_adminCommands); diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 17a2b29638b..1c1e8ca0e96 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -53,6 +53,7 @@ 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.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; @@ -790,9 +791,39 @@ public class ApiServer implements HttpRequestHandler { } private boolean isCommandAvailable(User user, String commandName) { + if (user == null) { + return false; + } + + Account account = _accountMgr.getAccount(user.getAccountId()); + if (account == null) { + return false; + } + + RoleType roleType = RoleType.Unknown; + 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; + default: + return false; + } + for (APIAccessChecker apiChecker : _apiAccessCheckers) { // Fail the checking if any checker fails to verify - if (!apiChecker.canAccessAPI(user, commandName)) + if (!apiChecker.canAccessAPI(roleType, commandName)) return false; } return true; From 1c59dae7087857858c91fc5497ee7a692833a2a2 Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Thu, 10 Jan 2013 14:13:58 -0700 Subject: [PATCH 20/73] Summary: Add devcloud-kvm files Detail: Working on getting a KVM-based devcloud so that development that requires the KVM hypervisor can be simpler. This adds some setup devcloud files. Signed-off-by: Marcus Sorensen 1357852438 -0700 --- pom.xml | 2 + tools/devcloud-kvm/README.md | 21 +++++ tools/devcloud-kvm/devcloud-kvm.cfg | 97 +++++++++++++++++++ tools/devcloud-kvm/devcloud-kvm.sql | 40 ++++++++ tools/devcloud-kvm/pom.xml | 138 ++++++++++++++++++++++++++++ 5 files changed, 298 insertions(+) create mode 100644 tools/devcloud-kvm/README.md create mode 100644 tools/devcloud-kvm/devcloud-kvm.cfg create mode 100644 tools/devcloud-kvm/devcloud-kvm.sql create mode 100644 tools/devcloud-kvm/pom.xml 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/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..eeba64153a3 --- /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="https://dl.dropbox.com/u/678991/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 + + + + + + + + From ffcc6d781ebbe7985251b9641de959a07a4ae6c8 Mon Sep 17 00:00:00 2001 From: Joe Brockmeier Date: Thu, 10 Jan 2013 16:01:02 -0600 Subject: [PATCH 21/73] CLOUDSTACK-683: Fixed missing image in Accessing VM Section --- docs/en-US/accessing-vms.xml | 2 +- docs/en-US/images/view-console-button.png | Bin 0 -> 59996 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 docs/en-US/images/view-console-button.png 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/images/view-console-button.png b/docs/en-US/images/view-console-button.png new file mode 100644 index 0000000000000000000000000000000000000000..b321ceadefee0eb00c7db1de14002c7df81b4b31 GIT binary patch literal 59996 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 Date: Thu, 10 Jan 2013 14:46:52 -0800 Subject: [PATCH 22/73] Keep removing --- agent/src/com/cloud/agent/AgentShell.java | 39 +- agent/src/com/cloud/agent/VmmAgentShell.java | 406 +++++++++--------- .../AgentComponentLibraryBase.java | 76 ---- .../test/com/cloud/agent/TestAgentShell.java | 13 +- .../ObjectInDataStoreManagerImpl.java | 2 + .../resource/LibvirtComputingResource.java | 281 ++++++------ server/src/com/cloud/api/ApiDispatcher.java | 4 + .../api/commands/ListTrafficMonitorsCmd.java | 28 +- .../com/cloud/storage/s3/S3ManagerImpl.java | 163 ++++--- usage/src/com/cloud/usage/UsageServer.java | 3 +- .../com/cloud/utils/db/TransactionTest.java | 8 +- .../utils/log/CglibThrowableRendererTest.java | 6 +- 12 files changed, 452 insertions(+), 577 deletions(-) delete mode 100755 agent/src/com/cloud/agent/configuration/AgentComponentLibraryBase.java diff --git a/agent/src/com/cloud/agent/AgentShell.java b/agent/src/com/cloud/agent/AgentShell.java index eac3e50d92c..0e020935e90 100644 --- a/agent/src/com/cloud/agent/AgentShell.java +++ b/agent/src/com/cloud/agent/AgentShell.java @@ -53,10 +53,7 @@ import com.cloud.utils.ProcessUtil; import com.cloud.utils.PropertiesUtil; import com.cloud.utils.backoff.BackoffAlgorithm; import com.cloud.utils.backoff.impl.ConstantTimeBackoff; -import com.cloud.utils.component.Adapters; -import com.cloud.utils.component.LegacyComponentLocator; import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.net.MacAddress; import com.cloud.utils.script.Script; public class AgentShell implements IAgentShell { @@ -146,6 +143,7 @@ public class AgentShell implements IAgentShell { return _guid; } + @Override public Map getCmdLineProperties() { return _cmdLineProperties; } @@ -378,8 +376,6 @@ public class AgentShell implements IAgentShell { public void init(String[] args) throws ConfigurationException { - final LegacyComponentLocator locator = LegacyComponentLocator.getLocator("agent"); - final Class c = this.getClass(); _version = c.getPackage().getImplementationVersion(); if (_version == null) { @@ -396,12 +392,9 @@ public class AgentShell implements IAgentShell { s_logger.debug("Found property: " + property); } - _storage = locator.getManager(StorageComponent.class); - if (_storage == null) { - s_logger.info("Defaulting to using properties file for storage"); - _storage = new PropertiesStorage(); - _storage.configure("Storage", new HashMap()); - } + s_logger.info("Defaulting to using properties file for storage"); + _storage = new PropertiesStorage(); + _storage.configure("Storage", new HashMap()); // merge with properties from command line to let resource access // command line parameters @@ -410,22 +403,9 @@ public class AgentShell implements IAgentShell { _properties.put(cmdLineProp.getKey(), cmdLineProp.getValue()); } - final Adapters adapters = locator.getAdapters(BackoffAlgorithm.class); - final Enumeration en = adapters.enumeration(); - while (en.hasMoreElements()) { - _backoff = (BackoffAlgorithm) en.nextElement(); - break; - } - if (en.hasMoreElements()) { - s_logger.info("More than one backoff algorithm specified. Using the first one "); - } - - if (_backoff == null) { - s_logger.info("Defaulting to the constant time backoff algorithm"); - _backoff = new ConstantTimeBackoff(); - _backoff.configure("ConstantTimeBackoff", - new HashMap()); - } + s_logger.info("Defaulting to the constant time backoff algorithm"); + _backoff = new ConstantTimeBackoff(); + _backoff.configure("ConstantTimeBackoff", new HashMap()); } private void launchAgent() throws ConfigurationException { @@ -469,6 +449,7 @@ public class AgentShell implements IAgentShell { openPortWithIptables(port); _consoleProxyMain = new Thread(new Runnable() { + @Override public void run() { try { Class consoleProxyClazz = Class.forName("com.cloud.consoleproxy.ConsoleProxy"); @@ -522,7 +503,7 @@ public class AgentShell implements IAgentShell { } catch (final SecurityException e) { throw new ConfigurationException( "Security excetion when loading resource: " + name - + " due to: " + e.toString()); + + " due to: " + e.toString()); } catch (final NoSuchMethodException e) { throw new ConfigurationException( "Method not found excetion when loading resource: " @@ -534,7 +515,7 @@ public class AgentShell implements IAgentShell { } catch (final InstantiationException e) { throw new ConfigurationException( "Instantiation excetion when loading resource: " + name - + " due to: " + e.toString()); + + " due to: " + e.toString()); } catch (final IllegalAccessException e) { throw new ConfigurationException( "Illegal access exception when loading resource: " diff --git a/agent/src/com/cloud/agent/VmmAgentShell.java b/agent/src/com/cloud/agent/VmmAgentShell.java index ec2867940a2..190d1168284 100644 --- a/agent/src/com/cloud/agent/VmmAgentShell.java +++ b/agent/src/com/cloud/agent/VmmAgentShell.java @@ -23,7 +23,6 @@ import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; -import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -41,19 +40,15 @@ import com.cloud.agent.dao.impl.PropertiesStorage; import com.cloud.agent.transport.Request; import com.cloud.resource.ServerResource; import com.cloud.utils.NumbersUtil; -import com.cloud.utils.ProcessUtil; import com.cloud.utils.PropertiesUtil; import com.cloud.utils.backoff.BackoffAlgorithm; import com.cloud.utils.backoff.impl.ConstantTimeBackoff; -import com.cloud.utils.component.Adapters; -import com.cloud.utils.component.LegacyComponentLocator; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.MacAddress; import com.cloud.utils.nio.HandlerFactory; import com.cloud.utils.nio.Link; import com.cloud.utils.nio.NioServer; import com.cloud.utils.nio.Task; -import com.cloud.utils.nio.Task.Type; /** * Implementation of agent shell to run the agents on System Center Virtual Machine manager @@ -61,7 +56,7 @@ import com.cloud.utils.nio.Task.Type; public class VmmAgentShell implements IAgentShell, HandlerFactory { - private static final Logger s_logger = Logger.getLogger(VmmAgentShell.class.getName()); + private static final Logger s_logger = Logger.getLogger(VmmAgentShell.class.getName()); private final Properties _properties = new Properties(); private final Map _cmdLineProperties = new HashMap(); private StorageComponent _storage; @@ -76,112 +71,112 @@ public class VmmAgentShell implements IAgentShell, HandlerFactory { private int _proxyPort; private int _workers; private String _guid; - static private NioServer _connection; - static private int _listenerPort=9000; + static private NioServer _connection; + static private int _listenerPort=9000; private int _nextAgentId = 1; private volatile boolean _exit = false; private int _pingRetries; - private Thread _consoleProxyMain = null; + private final Thread _consoleProxyMain = null; private final List _agents = new ArrayList(); public VmmAgentShell() { } - + @Override public Properties getProperties() { - return _properties; + return _properties; } - + @Override public BackoffAlgorithm getBackoffAlgorithm() { - return _backoff; + return _backoff; } - + @Override public int getPingRetries() { - return _pingRetries; + return _pingRetries; } - + @Override public String getZone() { - return _zone; + return _zone; } - + @Override public String getPod() { - return _pod; + return _pod; } - + @Override public String getHost() { - return _host; + return _host; } - + @Override public String getPrivateIp() { - return _privateIp; + return _privateIp; } - + @Override public int getPort() { - return _port; + return _port; } - + @Override public int getProxyPort() { - return _proxyPort; + return _proxyPort; } - + @Override public int getWorkers() { - return _workers; + return _workers; } - + @Override public String getGuid() { - return _guid; + return _guid; } - @Override - public void upgradeAgent(String url) { - // TODO Auto-generated method stub - - } + @Override + public void upgradeAgent(String url) { + // TODO Auto-generated method stub - @Override + } + + @Override public String getVersion() { - return _version; + return _version; } - @Override - public Map getCmdLineProperties() { - // TODO Auto-generated method stub - return _cmdLineProperties; - } - - public String getProperty(String prefix, String name) { - if(prefix != null) - return _properties.getProperty(prefix + "." + name); - - return _properties.getProperty(name); + @Override + public Map getCmdLineProperties() { + // TODO Auto-generated method stub + return _cmdLineProperties; } - - @Override - public String getPersistentProperty(String prefix, String name) { - if(prefix != null) - return _storage.get(prefix + "." + name); - return _storage.get(name); - } - @Override - public void setPersistentProperty(String prefix, String name, String value) { - if(prefix != null) - _storage.persist(prefix + "." + name, value); - else - _storage.persist(name, value); - } + public String getProperty(String prefix, String name) { + if(prefix != null) + return _properties.getProperty(prefix + "." + name); - private void loadProperties() throws ConfigurationException { + return _properties.getProperty(name); + } + + @Override + public String getPersistentProperty(String prefix, String name) { + if(prefix != null) + return _storage.get(prefix + "." + name); + return _storage.get(name); + } + + @Override + public void setPersistentProperty(String prefix, String name, String value) { + if(prefix != null) + _storage.persist(prefix + "." + name, value); + else + _storage.persist(name, value); + } + + private void loadProperties() throws ConfigurationException { final File file = PropertiesUtil.findConfigFile("agent.properties"); if (file == null) { throw new ConfigurationException("Unable to find agent.properties."); @@ -197,7 +192,7 @@ public class VmmAgentShell implements IAgentShell, HandlerFactory { throw new CloudRuntimeException("IOException in reading " + file.getAbsolutePath(), ex); } } - + protected boolean parseCommand(final String[] args) throws ConfigurationException { String host = null; String workers = null; @@ -211,7 +206,7 @@ public class VmmAgentShell implements IAgentShell, HandlerFactory { System.out.println("Invalid Parameter: " + args[i]); continue; } - + // save command line properties _cmdLineProperties.put(tokens[0], tokens[1]); @@ -222,14 +217,14 @@ public class VmmAgentShell implements IAgentShell, HandlerFactory { } else if (tokens[0].equalsIgnoreCase("host")) { host = tokens[1]; } else if(tokens[0].equalsIgnoreCase("zone")) { - zone = tokens[1]; + zone = tokens[1]; } else if(tokens[0].equalsIgnoreCase("pod")) { - pod = tokens[1]; + pod = tokens[1]; } else if(tokens[0].equalsIgnoreCase("guid")) { - guid = tokens[1]; - } else if(tokens[0].equalsIgnoreCase("eth1ip")) { - _privateIp = tokens[1]; - } + guid = tokens[1]; + } else if(tokens[0].equalsIgnoreCase("eth1ip")) { + _privateIp = tokens[1]; + } } if (port == null) { @@ -237,7 +232,7 @@ public class VmmAgentShell implements IAgentShell, HandlerFactory { } _port = NumbersUtil.parseInt(port, 8250); - + _proxyPort = NumbersUtil.parseInt(getProperty(null, "consoleproxy.httpListenPort"), 443); if (workers == null) { @@ -254,42 +249,42 @@ public class VmmAgentShell implements IAgentShell, HandlerFactory { host = "localhost"; } _host = host; - + if(zone != null) - _zone = zone; + _zone = zone; else - _zone = getProperty(null, "zone"); + _zone = getProperty(null, "zone"); if (_zone == null || (_zone.startsWith("@") && _zone.endsWith("@"))) { - _zone = "default"; + _zone = "default"; } if(pod != null) - _pod = pod; + _pod = pod; else - _pod = getProperty(null, "pod"); + _pod = getProperty(null, "pod"); if (_pod == null || (_pod.startsWith("@") && _pod.endsWith("@"))) { - _pod = "default"; + _pod = "default"; } if (_host == null || (_host.startsWith("@") && _host.endsWith("@"))) { throw new ConfigurationException("Host is not configured correctly: " + _host); } - + final String retries = getProperty(null, "ping.retries"); _pingRetries = NumbersUtil.parseInt(retries, 5); String value = getProperty(null, "developer"); boolean developer = Boolean.parseBoolean(value); - + if(guid != null) - _guid = guid; + _guid = guid; else - _guid = getProperty(null, "guid"); + _guid = getProperty(null, "guid"); if (_guid == null) { - if (!developer) { - throw new ConfigurationException("Unable to find the guid"); - } - _guid = MacAddress.getMacAddress().toString(":"); + if (!developer) { + throw new ConfigurationException("Unable to find the guid"); + } + _guid = MacAddress.getMacAddress().toString(":"); } return true; @@ -303,63 +298,46 @@ public class VmmAgentShell implements IAgentShell, HandlerFactory { } s_logger.trace("Launching agent based on type=" + typeInfo); } - + private void launchAgent() throws ConfigurationException { String resourceClassNames = getProperty(null, "resource"); s_logger.trace("resource=" + resourceClassNames); if(resourceClassNames != null) { - launchAgentFromClassInfo(resourceClassNames); - return; + launchAgentFromClassInfo(resourceClassNames); + return; } - + launchAgentFromTypeInfo(); } - + private void init(String[] args) throws ConfigurationException{ - - final LegacyComponentLocator locator = LegacyComponentLocator.getLocator("agent"); - + final Class c = this.getClass(); _version = c.getPackage().getImplementationVersion(); if (_version == null) { throw new CloudRuntimeException("Unable to find the implementation version of this agent"); } s_logger.info("Implementation Version is " + _version); - + parseCommand(args); - - _storage = locator.getManager(StorageComponent.class); - if (_storage == null) { - s_logger.info("Defaulting to using properties file for storage"); - _storage = new PropertiesStorage(); - _storage.configure("Storage", new HashMap()); - } + + s_logger.info("Defaulting to using properties file for storage"); + _storage = new PropertiesStorage(); + _storage.configure("Storage", new HashMap()); // merge with properties from command line to let resource access command line parameters for(Map.Entry cmdLineProp : getCmdLineProperties().entrySet()) { - _properties.put(cmdLineProp.getKey(), cmdLineProp.getValue()); - } - - final Adapters adapters = locator.getAdapters(BackoffAlgorithm.class); - final Enumeration en = adapters.enumeration(); - while (en.hasMoreElements()) { - _backoff = (BackoffAlgorithm)en.nextElement(); - break; - } - if (en.hasMoreElements()) { - s_logger.info("More than one backoff algorithm specified. Using the first one "); + _properties.put(cmdLineProp.getKey(), cmdLineProp.getValue()); } - if (_backoff == null) { - s_logger.info("Defaulting to the constant time backoff algorithm"); - _backoff = new ConstantTimeBackoff(); - _backoff.configure("ConstantTimeBackoff", new HashMap()); - } + s_logger.info("Defaulting to the constant time backoff algorithm"); + _backoff = new ConstantTimeBackoff(); + _backoff.configure("ConstantTimeBackoff", new HashMap()); } private void launchAgentFromClassInfo(String resourceClassNames) throws ConfigurationException { - String[] names = resourceClassNames.split("\\|"); - for(String name: names) { + String[] names = resourceClassNames.split("\\|"); + for(String name: names) { Class impl; try { impl = Class.forName(name); @@ -368,41 +346,41 @@ public class VmmAgentShell implements IAgentShell, HandlerFactory { ServerResource resource = (ServerResource)constructor.newInstance(); launchAgent(getNextAgentId(), resource); } catch (final ClassNotFoundException e) { - throw new ConfigurationException("Resource class not found: " + name); + throw new ConfigurationException("Resource class not found: " + name); } catch (final SecurityException e) { - throw new ConfigurationException("Security excetion when loading resource: " + name); + throw new ConfigurationException("Security excetion when loading resource: " + name); } catch (final NoSuchMethodException e) { - throw new ConfigurationException("Method not found excetion when loading resource: " + name); + throw new ConfigurationException("Method not found excetion when loading resource: " + name); } catch (final IllegalArgumentException e) { - throw new ConfigurationException("Illegal argument excetion when loading resource: " + name); + throw new ConfigurationException("Illegal argument excetion when loading resource: " + name); } catch (final InstantiationException e) { - throw new ConfigurationException("Instantiation excetion when loading resource: " + name); + throw new ConfigurationException("Instantiation excetion when loading resource: " + name); } catch (final IllegalAccessException e) { - throw new ConfigurationException("Illegal access exception when loading resource: " + name); + throw new ConfigurationException("Illegal access exception when loading resource: " + name); } catch (final InvocationTargetException e) { - throw new ConfigurationException("Invocation target exception when loading resource: " + name); + throw new ConfigurationException("Invocation target exception when loading resource: " + name); } - } + } } private void launchAgent(int localAgentId, ServerResource resource) throws ConfigurationException { - // we don't track agent after it is launched for now - Agent agent = new Agent(this, localAgentId, resource); - _agents.add(agent); - agent.start(); + // we don't track agent after it is launched for now + Agent agent = new Agent(this, localAgentId, resource); + _agents.add(agent); + agent.start(); } public synchronized int getNextAgentId() { - return _nextAgentId++; + return _nextAgentId++; } - - private void run(String[] args) { - - try { + + private void run(String[] args) { + + try { System.setProperty("java.net.preferIPv4Stack","true"); - loadProperties(); - init(args); - + loadProperties(); + init(args); + String instance = getProperty(null, "instance"); if (instance == null) { instance = ""; @@ -413,22 +391,22 @@ public class VmmAgentShell implements IAgentShell, HandlerFactory { // TODO need to do this check. For Agentshell running on windows needs different approach //final String run = "agent." + instance + "pid"; //s_logger.debug("Checking to see if " + run + "exists."); - //ProcessUtil.pidCheck(run); - - + //ProcessUtil.pidCheck(run); + + // TODO: For Hyper-V agent.properties need to be revamped to support multiple agents // corresponding to multiple clusters but running on a SCVMM host - + // read the persistent storage and launch the agents - //launchAgent(); + //launchAgent(); // FIXME get rid of this approach of agent listening for boot strap commands from the management server - // now listen for bootstrap request from the management server and launch agents - _connection = new NioServer("VmmAgentShell", _listenerPort, 1, this); - _connection.start(); - s_logger.info("SCVMM agent is listening on port " +_listenerPort + " for bootstrap command from management server"); - while(_connection.isRunning()); + // now listen for bootstrap request from the management server and launch agents + _connection = new NioServer("VmmAgentShell", _listenerPort, 1, this); + _connection.start(); + s_logger.info("SCVMM agent is listening on port " +_listenerPort + " for bootstrap command from management server"); + while(_connection.isRunning()); } catch(final ConfigurationException e) { s_logger.error("Unable to start agent: " + e.getMessage()); System.out.println("Unable to start agent: " + e.getMessage()); @@ -438,89 +416,89 @@ public class VmmAgentShell implements IAgentShell, HandlerFactory { System.out.println("Unable to start agent: " + e.getMessage()); System.exit(ExitStatus.Error.value()); } - } + } - @Override - public Task create(com.cloud.utils.nio.Task.Type type, Link link, - byte[] data) { - return new AgentBootStrapHandler(type, link, data); - } + @Override + public Task create(com.cloud.utils.nio.Task.Type type, Link link, + byte[] data) { + return new AgentBootStrapHandler(type, link, data); + } - public void stop() { - _exit = true; - if(_consoleProxyMain != null) { - _consoleProxyMain.interrupt(); - } - } - - public static void main(String[] args) { - - VmmAgentShell shell = new VmmAgentShell(); - Runtime.getRuntime().addShutdownHook(new ShutdownThread(shell)); - shell.run(args); - } + public void stop() { + _exit = true; + if(_consoleProxyMain != null) { + _consoleProxyMain.interrupt(); + } + } - // class to handle the bootstrap command from the management server - private class AgentBootStrapHandler extends Task { + public static void main(String[] args) { - public AgentBootStrapHandler(Task.Type type, Link link, byte[] data) { - super(type, link, data); - } + VmmAgentShell shell = new VmmAgentShell(); + Runtime.getRuntime().addShutdownHook(new ShutdownThread(shell)); + shell.run(args); + } - @Override - protected void doTask(Task task) throws Exception { - final Type type = task.getType(); - s_logger.info("recieved task of type "+ type.toString() +" to handle in BootStrapTakHandler"); - if (type == Task.Type.DATA) - { - final byte[] data = task.getData(); - final Request request = Request.parse(data); - final Command cmd = request.getCommand(); - - if (cmd instanceof StartupVMMAgentCommand) { + // class to handle the bootstrap command from the management server + private class AgentBootStrapHandler extends Task { - StartupVMMAgentCommand vmmCmd = (StartupVMMAgentCommand) cmd; + public AgentBootStrapHandler(Task.Type type, Link link, byte[] data) { + super(type, link, data); + } - _zone = Long.toString(vmmCmd.getDataCenter()); - _cmdLineProperties.put("zone", _zone); + @Override + protected void doTask(Task task) throws Exception { + final Type type = task.getType(); + s_logger.info("recieved task of type "+ type.toString() +" to handle in BootStrapTakHandler"); + if (type == Task.Type.DATA) + { + final byte[] data = task.getData(); + final Request request = Request.parse(data); + final Command cmd = request.getCommand(); - _pod = Long.toString(vmmCmd.getPod()); - _cmdLineProperties.put("pod", _pod); + if (cmd instanceof StartupVMMAgentCommand) { - _cluster = vmmCmd.getClusterName(); - _cmdLineProperties.put("cluster", _cluster); + StartupVMMAgentCommand vmmCmd = (StartupVMMAgentCommand) cmd; - _guid = vmmCmd.getGuid(); - _cmdLineProperties.put("guid", _guid); + _zone = Long.toString(vmmCmd.getDataCenter()); + _cmdLineProperties.put("zone", _zone); - _host = vmmCmd.getManagementServerIP(); - _port = NumbersUtil.parseInt(vmmCmd.getport(), 8250); + _pod = Long.toString(vmmCmd.getPod()); + _cmdLineProperties.put("pod", _pod); - s_logger.info("Recieved boot strap command from management server with parameters " + - " Zone:"+ _zone + " "+ - " Cluster:"+ _cluster + " "+ - " pod:"+_pod + " "+ - " host:"+ _host +" "+ - " port:"+_port); + _cluster = vmmCmd.getClusterName(); + _cmdLineProperties.put("cluster", _cluster); - launchAgentFromClassInfo("com.cloud.hypervisor.hyperv.resource.HypervResource"); - - // TODO: persist the info in agent.properties for agent restarts - } - } - } - } + _guid = vmmCmd.getGuid(); + _cmdLineProperties.put("guid", _guid); + + _host = vmmCmd.getManagementServerIP(); + _port = NumbersUtil.parseInt(vmmCmd.getport(), 8250); + + s_logger.info("Recieved boot strap command from management server with parameters " + + " Zone:"+ _zone + " "+ + " Cluster:"+ _cluster + " "+ + " pod:"+_pod + " "+ + " host:"+ _host +" "+ + " port:"+_port); + + launchAgentFromClassInfo("com.cloud.hypervisor.hyperv.resource.HypervResource"); + + // TODO: persist the info in agent.properties for agent restarts + } + } + } + } private static class ShutdownThread extends Thread { - VmmAgentShell _shell; + VmmAgentShell _shell; public ShutdownThread(VmmAgentShell shell) { this._shell = shell; } - + @Override public void run() { _shell.stop(); } } - + } \ No newline at end of file diff --git a/agent/src/com/cloud/agent/configuration/AgentComponentLibraryBase.java b/agent/src/com/cloud/agent/configuration/AgentComponentLibraryBase.java deleted file mode 100755 index 4ea101c3951..00000000000 --- a/agent/src/com/cloud/agent/configuration/AgentComponentLibraryBase.java +++ /dev/null @@ -1,76 +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. -package com.cloud.agent.configuration; - -import java.util.List; -import java.util.Map; - -import com.cloud.utils.component.Adapter; -import com.cloud.utils.component.ComponentLibraryBase; -import com.cloud.utils.component.LegacyComponentLocator.ComponentInfo; -import com.cloud.utils.component.Manager; -import com.cloud.utils.component.PluggableService; -import com.cloud.utils.db.GenericDao; - -public class AgentComponentLibraryBase extends ComponentLibraryBase { - @Override - public Map>> getDaos() { - return null; - } - - @Override - public Map> getManagers() { - if (_managers.size() == 0) { - populateManagers(); - } - return _managers; - } - - @Override - public Map>> getAdapters() { - if (_adapters.size() == 0) { - populateAdapters(); - } - return _adapters; - } - - @Override - public Map, Class> getFactories() { - return null; - } - - protected void populateManagers() { - // addManager("StackMaidManager", StackMaidManagerImpl.class); - } - - protected void populateAdapters() { - - } - - protected void populateServices() { - - } - - @Override - public Map> getPluggableServices() { - if (_pluggableServices.size() == 0) { - populateServices(); - } - return _pluggableServices; - } - -} diff --git a/agent/test/com/cloud/agent/TestAgentShell.java b/agent/test/com/cloud/agent/TestAgentShell.java index d7210acbef3..0e9be0f1312 100644 --- a/agent/test/com/cloud/agent/TestAgentShell.java +++ b/agent/test/com/cloud/agent/TestAgentShell.java @@ -19,24 +19,23 @@ package com.cloud.agent; import java.io.File; import java.io.IOException; +import junit.framework.TestCase; + import org.apache.log4j.Logger; -import com.cloud.agent.AgentShell; -import com.cloud.utils.testcase.Log4jEnabledTestCase; - -public class TestAgentShell extends Log4jEnabledTestCase { +public class TestAgentShell extends TestCase { protected final static Logger s_logger = Logger.getLogger(TestAgentShell.class); - + public void testWget() { File file = null; try { file = File.createTempFile("wget", ".html"); AgentShell.wget("http://www.google.com/", file); - + if (s_logger.isDebugEnabled()) { s_logger.debug("file saved to " + file.getAbsolutePath()); } - + } catch (final IOException e) { s_logger.warn("Exception while downloading agent update package, ", e); } diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java index 26cd5592bab..7c310b777f3 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java @@ -1,5 +1,7 @@ package org.apache.cloudstack.storage.datastore; +import javax.inject.Inject; + import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.storage.db.ObjectInDataStoreDao; import org.apache.cloudstack.storage.db.ObjectInDataStoreVO; diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index cd1971dd427..af5b2a4c33b 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -25,9 +25,7 @@ import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; -import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; @@ -44,15 +42,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.ejb.Local; import javax.naming.ConfigurationException; @@ -167,7 +164,13 @@ import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; +import com.cloud.agent.resource.virtualnetwork.VirtualRoutingResource; +import com.cloud.dc.Vlan; +import com.cloud.exception.InternalErrorException; +import com.cloud.host.Host.Type; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.kvm.resource.KVMHABase.NfsStoragePool; +import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ClockDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ConsoleDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.CpuTuneDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DevicesDef; @@ -182,16 +185,10 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.hostNicType; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SerialDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.TermPolicy; -import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ClockDef; -import com.cloud.agent.resource.virtualnetwork.VirtualRoutingResource; import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk; import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk.PhysicalDiskFormat; import com.cloud.hypervisor.kvm.storage.KVMStoragePool; import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; -import com.cloud.dc.Vlan; -import com.cloud.exception.InternalErrorException; -import com.cloud.host.Host.Type; -import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.IsolationType; import com.cloud.network.Networks.RouterPrivateIpStrategy; @@ -199,6 +196,7 @@ import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetworkSetupInfo; import com.cloud.resource.ServerResource; import com.cloud.resource.ServerResourceBase; +import com.cloud.storage.JavaStorageLayer; import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; @@ -212,7 +210,6 @@ import com.cloud.storage.template.TemplateLocation; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.PropertiesUtil; - import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.utils.script.OutputInterpreter; @@ -246,7 +243,7 @@ import com.cloud.vm.VirtualMachineName; **/ @Local(value = { ServerResource.class }) public class LibvirtComputingResource extends ServerResourceBase implements - ServerResource { +ServerResource { private static final Logger s_logger = Logger .getLogger(LibvirtComputingResource.class); @@ -329,8 +326,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements private boolean _can_bridge_firewall; protected String _localStoragePath; protected String _localStorageUUID; - private Map _pifs = new HashMap(); - private Map> hostNetInfo = new HashMap>(); + private final Map _pifs = new HashMap(); + private final Map> hostNetInfo = new HashMap>(); private final Map _vmStats = new ConcurrentHashMap(); protected boolean _disconnected = true; @@ -375,7 +372,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements tokens[3] = Integer.toString(lastbyte); StringBuilder end = new StringBuilder(15); end.append(tokens[0]).append(".").append(tokens[1]).append(".") - .append(tokens[2]).append(".").append(tokens[3]); + .append(tokens[2]).append(".").append(tokens[3]); return end.toString(); } @@ -444,16 +441,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements return false; } - try { - Class clazz = Class - .forName("com.cloud.storage.JavaStorageLayer"); - _storage = (StorageLayer) ComponentLocator.inject(clazz); - _storage.configure("StorageLayer", params); - } catch (ClassNotFoundException e) { - throw new ConfigurationException("Unable to find class " - + "com.cloud.storage.JavaStorageLayer"); - } - + _storage = new JavaStorageLayer(); + _storage.configure("StorageLayer", params); String domrScriptsDir = (String) params.get("domr.scripts.dir"); if (domrScriptsDir == null) { @@ -685,7 +674,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements String[] isoPaths = { "/usr/lib64/cloud/agent/vms/systemvm.iso", "/usr/lib/cloud/agent/vms/systemvm.iso", "/usr/lib64/cloud/common/vms/systemvm.iso", - "/usr/lib/cloud/common/vms/systemvm.iso" }; + "/usr/lib/cloud/common/vms/systemvm.iso" }; for (String isoPath : isoPaths) { if (_storage.exists(isoPath)) { _sysvmISOPath = isoPath; @@ -723,7 +712,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements if (_mountPoint == null) { _mountPoint = "/mnt"; } - + value = (String) params.get("vm.migrate.speed"); _migrateSpeed = NumbersUtil.parseInt(value, -1); if (_migrateSpeed == -1) { @@ -736,7 +725,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements try { _migrateSpeed = Integer.parseInt(tokens[0]); } catch (Exception e) { - + } s_logger.debug("device " + _pifs.get("public") + " has speed: " + String.valueOf(_migrateSpeed)); } @@ -750,28 +739,28 @@ public class LibvirtComputingResource extends ServerResourceBase implements bridges.put("private", _privBridgeName); bridges.put("guest", _guestBridgeName); - params.put("libvirt.host.bridges", (Object) bridges); - params.put("libvirt.host.pifs", (Object) _pifs); + params.put("libvirt.host.bridges", bridges); + params.put("libvirt.host.pifs", _pifs); // Load the vif driver String vifDriverName = (String) params.get("libvirt.vif.driver"); if (vifDriverName == null) { - s_logger.info("No libvirt.vif.driver specififed. Defaults to BridgeVifDriver."); - vifDriverName = "com.cloud.hypervisor.kvm.resource.BridgeVifDriver"; + s_logger.info("No libvirt.vif.driver specififed. Defaults to BridgeVifDriver."); + vifDriverName = "com.cloud.hypervisor.kvm.resource.BridgeVifDriver"; } - params.put("libvirt.computing.resource", (Object) this); + params.put("libvirt.computing.resource", this); try { - Class clazz = Class.forName(vifDriverName); - _vifDriver = (VifDriver) clazz.newInstance(); - _vifDriver.configure(params); + Class clazz = Class.forName(vifDriverName); + _vifDriver = (VifDriver) clazz.newInstance(); + _vifDriver.configure(params); } catch (ClassNotFoundException e) { - throw new ConfigurationException("Unable to find class for libvirt.vif.driver " + e); + throw new ConfigurationException("Unable to find class for libvirt.vif.driver " + e); } catch (InstantiationException e) { - throw new ConfigurationException("Unable to instantiate class for libvirt.vif.driver " + e); + throw new ConfigurationException("Unable to instantiate class for libvirt.vif.driver " + e); } catch (Exception e) { - throw new ConfigurationException("Failed to initialize libvirt.vif.driver " + e); + throw new ConfigurationException("Failed to initialize libvirt.vif.driver " + e); } @@ -802,7 +791,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements String vlan = Script.runSimpleBashScript("ls /proc/net/vlan/" + pif); if (vlan != null && !vlan.isEmpty()) { - pif = Script.runSimpleBashScript("grep ^Device\\: /proc/net/vlan/" + pif + " | awk {'print $2'}"); + pif = Script.runSimpleBashScript("grep ^Device\\: /proc/net/vlan/" + pif + " | awk {'print $2'}"); } return pif; @@ -1105,8 +1094,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements KVMStoragePool secondaryStoragePool = null; try { KVMStoragePool primaryPool = _storagePoolMgr.getStoragePool( - pool.getType(), - pool.getUuid()); + pool.getType(), + pool.getUuid()); String volumeName = UUID.randomUUID().toString(); if (copyToSecondary) { @@ -1116,20 +1105,20 @@ public class LibvirtComputingResource extends ServerResourceBase implements String volumeDestPath = "/volumes/" + cmd.getVolumeId() + File.separator; secondaryStoragePool = _storagePoolMgr.getStoragePoolByURI( - secondaryStorageUrl); + secondaryStorageUrl); secondaryStoragePool.createFolder(volumeDestPath); secondaryStoragePool.delete(); secondaryStoragePool = _storagePoolMgr.getStoragePoolByURI( - secondaryStorageUrl - + volumeDestPath); + secondaryStorageUrl + + volumeDestPath); _storagePoolMgr.copyPhysicalDisk(volume, destVolumeName,secondaryStoragePool); return new CopyVolumeAnswer(cmd, true, null, null, volumeName); } else { volumePath = "/volumes/" + cmd.getVolumeId() + File.separator; secondaryStoragePool = _storagePoolMgr.getStoragePoolByURI( - secondaryStorageUrl - + volumePath); + secondaryStorageUrl + + volumePath); KVMPhysicalDisk volume = secondaryStoragePool .getPhysicalDisk(cmd.getVolumePath() + ".qcow2"); _storagePoolMgr.copyPhysicalDisk(volume, volumeName, @@ -1148,7 +1137,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements protected Answer execute(DeleteStoragePoolCommand cmd) { try { _storagePoolMgr.deleteStoragePool(cmd.getPool().getType(), - cmd.getPool().getUuid()); + cmd.getPool().getUuid()); return new Answer(cmd); } catch (CloudRuntimeException e) { return new Answer(cmd, false, e.toString()); @@ -1190,7 +1179,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements long disksize; try { primaryPool = _storagePoolMgr.getStoragePool(pool.getType(), - pool.getUuid()); + pool.getUuid()); disksize = dskch.getSize(); if (cmd.getTemplateUrl() != null) { @@ -1199,7 +1188,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements } else { BaseVol = primaryPool.getPhysicalDisk(cmd.getTemplateUrl()); vol = _storagePoolMgr.createDiskFromTemplate(BaseVol, UUID - .randomUUID().toString(), primaryPool); + .randomUUID().toString(), primaryPool); } if (vol == null) { return new Answer(cmd, false, @@ -1273,8 +1262,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements try { KVMStoragePool pool = _storagePoolMgr.getStoragePool( - vol.getPoolType(), - vol.getPoolUuid()); + vol.getPoolType(), + vol.getPoolUuid()); pool.deletePhysicalDisk(vol.getPath()); String vmName = cmd.getVmName(); String poolPath = pool.getLocalPath(); @@ -1289,7 +1278,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements _storagePoolMgr.deleteVbdByPath(vol.getPoolType(),patchVbd.getAbsolutePath()); } catch(CloudRuntimeException e) { s_logger.warn("unable to destroy patch disk '" + patchVbd.getAbsolutePath() + - "' while removing root disk for " + vmName + " : " + e); + "' while removing root disk for " + vmName + " : " + e); } } else { s_logger.debug("file '" +patchVbd.getAbsolutePath()+ "' not found"); @@ -1425,7 +1414,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements String dev = "eth" + nic.getDeviceId(); String netmask = NetUtils.getSubNet(routerGIP, nic.getNetmask()); String result = _virtRouterResource.assignGuestNetwork(dev, routerIP, - routerGIP, gateway, cidr, netmask, dns, domainName ); + routerGIP, gateway, cidr, netmask, dns, domainName ); if (result != null) { return new SetupGuestNetworkAnswer(cmd, false, "Creating guest network failed due to " + result); @@ -1461,7 +1450,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements String rule = sb.toString(); String result = _virtRouterResource.assignNetworkACL(routerIp, - dev, nic.getIp(), netmask, rule); + dev, nic.getIp(), netmask, rule); if (result != null) { for (int i=0; i < results.length; i++) { @@ -1492,21 +1481,21 @@ public class LibvirtComputingResource extends ServerResourceBase implements List pluggedNics = getInterfaces(conn, routerName); for (InterfaceDef pluggedNic : pluggedNics) { - String pluggedVlanBr = pluggedNic.getBrName(); - String pluggedVlanId = getVlanIdFromBridge(pluggedVlanBr); - if (pubVlan.equalsIgnoreCase(Vlan.UNTAGGED) - && pluggedVlanBr.equalsIgnoreCase(_publicBridgeName)) { - break; - } else if (pluggedVlanBr.equalsIgnoreCase(_linkLocalBridgeName)){ - /*skip over, no physical bridge device exists*/ - } else if (pluggedVlanId == null) { - /*this should only be true in the case of link local bridge*/ - return new SetSourceNatAnswer(cmd, false, "unable to find the vlan id for bridge "+pluggedVlanBr+ - " when attempting to set up" + pubVlan + " on router " + routerName); - } else if (pluggedVlanId.equals(pubVlan)) { - break; - } - devNum++; + String pluggedVlanBr = pluggedNic.getBrName(); + String pluggedVlanId = getVlanIdFromBridge(pluggedVlanBr); + if (pubVlan.equalsIgnoreCase(Vlan.UNTAGGED) + && pluggedVlanBr.equalsIgnoreCase(_publicBridgeName)) { + break; + } else if (pluggedVlanBr.equalsIgnoreCase(_linkLocalBridgeName)){ + /*skip over, no physical bridge device exists*/ + } else if (pluggedVlanId == null) { + /*this should only be true in the case of link local bridge*/ + return new SetSourceNatAnswer(cmd, false, "unable to find the vlan id for bridge "+pluggedVlanBr+ + " when attempting to set up" + pubVlan + " on router " + routerName); + } else if (pluggedVlanId.equals(pubVlan)) { + break; + } + devNum++; } String dev = "eth" + devNum; @@ -1544,8 +1533,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements vlanToNicNum.put("LinkLocal",devNum); } else if (pluggedVlan.equalsIgnoreCase(_publicBridgeName) - || pluggedVlan.equalsIgnoreCase(_privBridgeName) - || pluggedVlan.equalsIgnoreCase(_guestBridgeName)) { + || pluggedVlan.equalsIgnoreCase(_privBridgeName) + || pluggedVlan.equalsIgnoreCase(_guestBridgeName)) { vlanToNicNum.put(Vlan.UNTAGGED,devNum); } else { @@ -1560,7 +1549,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements String netmask = Long.toString(NetUtils.getCidrSize(ip.getVlanNetmask())); String subnet = NetUtils.getSubNet(ip.getPublicIp(), ip.getVlanNetmask()); _virtRouterResource.assignVpcIpToRouter(routerIP, ip.isAdd(), ip.getPublicIp(), - nicName, ip.getVlanGateway(), netmask, subnet); + nicName, ip.getVlanGateway(), netmask, subnet); results[i++] = ip.getPublicIp() + " - success"; } @@ -1587,14 +1576,14 @@ public class LibvirtComputingResource extends ServerResourceBase implements if (nic.getBrName().equalsIgnoreCase(_linkLocalBridgeName)) { vlanAllocatedToVM.put("LinkLocal", nicPos); } else { - if (nic.getBrName().equalsIgnoreCase(_publicBridgeName) - || nic.getBrName().equalsIgnoreCase(_privBridgeName) - || nic.getBrName().equalsIgnoreCase(_guestBridgeName)) { - vlanAllocatedToVM.put(Vlan.UNTAGGED, nicPos); - } else { - String vlanId = getVlanIdFromBridge(nic.getBrName()); - vlanAllocatedToVM.put(vlanId, nicPos); - } + if (nic.getBrName().equalsIgnoreCase(_publicBridgeName) + || nic.getBrName().equalsIgnoreCase(_privBridgeName) + || nic.getBrName().equalsIgnoreCase(_guestBridgeName)) { + vlanAllocatedToVM.put(Vlan.UNTAGGED, nicPos); + } else { + String vlanId = getVlanIdFromBridge(nic.getBrName()); + vlanAllocatedToVM.put(vlanId, nicPos); + } } nicPos++; } @@ -1649,13 +1638,13 @@ public class LibvirtComputingResource extends ServerResourceBase implements } KVMStoragePool primaryPool = _storagePoolMgr.getStoragePool( - cmd.getPool().getType(), - cmd.getPool().getUuid()); + cmd.getPool().getType(), + cmd.getPool().getUuid()); if (primaryPool.getType() == StoragePoolType.RBD) { s_logger.debug("Snapshots are not supported on RBD volumes"); return new ManageSnapshotAnswer(cmd, false, - "Snapshots are not supported on RBD volumes"); + "Snapshots are not supported on RBD volumes"); } KVMPhysicalDisk disk = primaryPool.getPhysicalDisk(cmd @@ -1728,7 +1717,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Connect conn = LibvirtConnection.getConnection(); secondaryStoragePool = _storagePoolMgr.getStoragePoolByURI( - secondaryStoragePoolUrl); + secondaryStoragePoolUrl); String ssPmountPath = secondaryStoragePool.getLocalPath(); snapshotRelPath = File.separator + "snapshots" + File.separator @@ -1739,8 +1728,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements + File.separator + dcId + File.separator + accountId + File.separator + volumeId; KVMStoragePool primaryPool = _storagePoolMgr.getStoragePool( - cmd.getPool().getType(), - cmd.getPrimaryStoragePoolNameLabel()); + cmd.getPool().getType(), + cmd.getPrimaryStoragePoolNameLabel()); KVMPhysicalDisk snapshotDisk = primaryPool.getPhysicalDisk(cmd .getVolumePath()); Script command = new Script(_manageSnapshotPath, _cmdsTimeout, @@ -1768,8 +1757,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements } KVMStoragePool primaryStorage = _storagePoolMgr.getStoragePool( - cmd.getPool().getType(), - cmd.getPool().getUuid()); + cmd.getPool().getType(), + cmd.getPool().getUuid()); if (state == DomainInfo.DomainState.VIR_DOMAIN_RUNNING && !primaryStorage.isExternalSnapshot()) { String vmUuid = vm.getUUIDString(); @@ -1853,7 +1842,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements KVMStoragePool secondaryStoragePool = null; try { secondaryStoragePool = _storagePoolMgr.getStoragePoolByURI(cmd - .getSecondaryStorageUrl()); + .getSecondaryStorageUrl()); String ssPmountPath = secondaryStoragePool.getLocalPath(); String snapshotDestPath = ssPmountPath + File.separator @@ -1884,15 +1873,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements int index = snapshotPath.lastIndexOf("/"); snapshotPath = snapshotPath.substring(0, index); KVMStoragePool secondaryPool = _storagePoolMgr.getStoragePoolByURI( - cmd.getSecondaryStorageUrl() - + snapshotPath); + cmd.getSecondaryStorageUrl() + + snapshotPath); KVMPhysicalDisk snapshot = secondaryPool.getPhysicalDisk(cmd .getSnapshotName()); String primaryUuid = cmd.getPrimaryStoragePoolNameLabel(); KVMStoragePool primaryPool = _storagePoolMgr .getStoragePool(cmd.getPool().getType(), - primaryUuid); + primaryUuid); String volUuid = UUID.randomUUID().toString(); KVMPhysicalDisk disk = _storagePoolMgr.copyPhysicalDisk(snapshot, volUuid, primaryPool); @@ -1928,7 +1917,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements .getSnapshotName()); secondaryPool = _storagePoolMgr.getStoragePoolByURI( - cmd.getSecondaryStorageUrl()); + cmd.getSecondaryStorageUrl()); String templatePath = secondaryPool.getLocalPath() + File.separator + templateInstallFolder; @@ -1978,8 +1967,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements protected GetStorageStatsAnswer execute(final GetStorageStatsCommand cmd) { try { KVMStoragePool sp = _storagePoolMgr.getStoragePool( - cmd.getPooltype(), - cmd.getStorageId()); + cmd.getPooltype(), + cmd.getStorageId()); return new GetStorageStatsAnswer(cmd, sp.getCapacity(), sp.getUsed()); } catch (CloudRuntimeException e) { @@ -1999,11 +1988,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements String templateInstallFolder = "/template/tmpl/" + templateFolder; secondaryStorage = _storagePoolMgr.getStoragePoolByURI( - secondaryStorageURL); + secondaryStorageURL); KVMStoragePool primary = _storagePoolMgr.getStoragePool( - cmd.getPool().getType(), - cmd.getPrimaryStoragePoolNameLabel()); + cmd.getPool().getType(), + cmd.getPrimaryStoragePoolNameLabel()); KVMPhysicalDisk disk = primary.getPhysicalDisk(cmd.getVolumePath()); String tmpltPath = secondaryStorage.getLocalPath() + File.separator + templateInstallFolder; @@ -2024,12 +2013,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements } else { s_logger.debug("Converting RBD disk " + disk.getPath() + " into template " + cmd.getUniqueName()); Script.runSimpleBashScript("qemu-img convert" - + " -f raw -O qcow2 " - + KVMPhysicalDisk.RBDStringBuilder(primary.getSourceHost(), - primary.getSourcePort(), - primary.getAuthUserName(), - primary.getAuthSecret(), - disk.getPath()) + + " -f raw -O qcow2 " + + KVMPhysicalDisk.RBDStringBuilder(primary.getSourceHost(), + primary.getSourcePort(), + primary.getAuthUserName(), + primary.getAuthSecret(), + disk.getPath()) + " " + tmpltPath + "/" + cmd.getUniqueName() + ".qcow2"); File templateProp = new File(tmpltPath + "/template.properties"); if (!templateProp.exists()) { @@ -2126,8 +2115,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements /* Copy volume to primary storage */ KVMStoragePool primaryPool = _storagePoolMgr.getStoragePool( - cmd.getPool().getType(), - cmd.getPoolUuid()); + cmd.getPool().getType(), + cmd.getPoolUuid()); KVMPhysicalDisk primaryVol = _storagePoolMgr.copyPhysicalDisk( tmplVol, UUID.randomUUID().toString(), primaryPool); @@ -2233,7 +2222,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements final StringBuffer sb = new StringBuffer(); sb.append("http://").append(proxyManagementIp).append(":" + cmdPort) - .append("/cmd/getstatus"); + .append("/cmd/getstatus"); boolean success = true; try { @@ -2291,8 +2280,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements try { Connect conn = LibvirtConnection.getConnection(); KVMStoragePool primary = _storagePoolMgr.getStoragePool( - cmd.getPooltype(), - cmd.getPoolUuid()); + cmd.getPooltype(), + cmd.getPoolUuid()); KVMPhysicalDisk disk = primary.getPhysicalDisk(cmd.getVolumePath()); attachOrDetachDisk(conn, cmd.getAttach(), cmd.getVmName(), disk, cmd.getDeviceId().intValue()); @@ -2364,10 +2353,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements private Answer execute(PingTestCommand cmd) { String result = null; final String computingHostIp = cmd.getComputingHostIp(); // TODO, split - // the - // command - // into 2 - // types + // the + // command + // into 2 + // types if (computingHostIp != null) { result = doPingTest(computingHostIp); @@ -2507,7 +2496,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements final Script cpuScript = new Script("/bin/bash", s_logger); cpuScript.add("-c"); cpuScript - .add("idle=$(top -b -n 1|grep Cpu\\(s\\):|cut -d% -f4|cut -d, -f2);echo $idle"); + .add("idle=$(top -b -n 1|grep Cpu\\(s\\):|cut -d% -f4|cut -d, -f2);echo $idle"); final OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser(); String result = cpuScript.execute(parser); @@ -2521,7 +2510,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements final Script memScript = new Script("/bin/bash", s_logger); memScript.add("-c"); memScript - .add("freeMem=$(free|grep cache:|awk '{print $4}');echo $freeMem"); + .add("freeMem=$(free|grep cache:|awk '{print $4}');echo $freeMem"); final OutputInterpreter.OneLineParser Memparser = new OutputInterpreter.OneLineParser(); result = memScript.execute(Memparser); if (result != null) { @@ -2721,7 +2710,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements script.add("-m","700"); script.add(_SSHKEYSPATH); script.execute(); - + if(!sshKeysDir.exists()) { s_logger.debug("failed to create directory " + _SSHKEYSPATH); } @@ -2903,7 +2892,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements for (NicTO nic : nics) { if (nic.getIsolationUri() != null && nic.getIsolationUri().getScheme() - .equalsIgnoreCase(IsolationType.Ec2.toString())) { + .equalsIgnoreCase(IsolationType.Ec2.toString())) { if (vmSpec.getType() != VirtualMachine.Type.User) { default_network_rules_for_systemvm(conn, vmName); break; @@ -2940,7 +2929,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements String path = isoPath.substring(0, index); String name = isoPath.substring(index + 1); KVMStoragePool secondaryPool = _storagePoolMgr.getStoragePoolByURI( - path); + path); KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name); return isoVol.getPath(); } else { @@ -2958,7 +2947,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements return arg0.getDeviceId() > arg1.getDeviceId() ? 1 : -1; } }); - + for (VolumeTO volume : disks) { KVMPhysicalDisk physicalDisk = null; KVMStoragePool pool = null; @@ -2968,12 +2957,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements String volDir = volPath.substring(0, index); String volName = volPath.substring(index + 1); KVMStoragePool secondaryStorage = _storagePoolMgr. - getStoragePoolByURI(volDir); + getStoragePoolByURI(volDir); physicalDisk = secondaryStorage.getPhysicalDisk(volName); } else if (volume.getType() != Volume.Type.ISO) { pool = _storagePoolMgr.getStoragePool( - volume.getPoolType(), - volume.getPoolUuid()); + volume.getPoolType(), + volume.getPoolUuid()); physicalDisk = pool.getPhysicalDisk(volume.getPath()); } @@ -2999,23 +2988,23 @@ public class LibvirtComputingResource extends ServerResourceBase implements For RBD pools we use the secret mechanism in libvirt. We store the secret under the UUID of the pool, that's why we pass the pool's UUID as the authSecret - */ + */ disk.defNetworkBasedDisk(physicalDisk.getPath().replace("rbd:", ""), pool.getSourceHost(), pool.getSourcePort(), - pool.getAuthUserName(), pool.getUuid(), - devId, diskBusType, diskProtocol.RBD); + pool.getAuthUserName(), pool.getUuid(), + devId, diskBusType, diskProtocol.RBD); } else if (pool.getType() == StoragePoolType.CLVM) { disk.defBlockBasedDisk(physicalDisk.getPath(), devId, - diskBusType); + diskBusType); } else { if (volume.getType() == Volume.Type.DATADISK) { - disk.defFileBasedDisk(physicalDisk.getPath(), devId, - DiskDef.diskBus.VIRTIO, - DiskDef.diskFmtType.QCOW2); - } else { - disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusType, DiskDef.diskFmtType.QCOW2); - } + disk.defFileBasedDisk(physicalDisk.getPath(), devId, + DiskDef.diskBus.VIRTIO, + DiskDef.diskFmtType.QCOW2); + } else { + disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusType, DiskDef.diskFmtType.QCOW2); + } - } + } } @@ -3052,8 +3041,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements VolumeTO rootVol = getVolume(vmSpec, Volume.Type.ROOT); String patchName = vmName + "-patchdisk"; KVMStoragePool pool = _storagePoolMgr.getStoragePool( - rootVol.getPoolType(), - rootVol.getPoolUuid()); + rootVol.getPoolType(), + rootVol.getPoolUuid()); String patchDiskPath = pool.getLocalPath() + "/" + patchName; List phyDisks = pool.listPhysicalDisks(); @@ -3069,7 +3058,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements if (!foundDisk) { s_logger.debug("generating new patch disk for " + vmName + " since none was found"); KVMPhysicalDisk disk = pool.createPhysicalDisk(patchName, KVMPhysicalDisk.PhysicalDiskFormat.RAW, - 10L * 1024 * 1024); + 10L * 1024 * 1024); } else { s_logger.debug("found existing patch disk at " + patchDiskPath + " using it for " + vmName); } @@ -3091,9 +3080,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements patchDisk.defBlockBasedDisk(patchDiskPath, 1, rootDisk.getBusType()); } else { patchDisk.defFileBasedDisk(patchDiskPath, 1, rootDisk.getBusType(), - DiskDef.diskFmtType.RAW); + DiskDef.diskFmtType.RAW); } - + disks.add(patchDisk); String bootArgs = vmSpec.getBootArgs(); @@ -3162,14 +3151,14 @@ public class LibvirtComputingResource extends ServerResourceBase implements protected synchronized String attachOrDetachISO(Connect conn, String vmName, String isoPath, boolean isAttach) - throws LibvirtException, URISyntaxException, InternalErrorException { + throws LibvirtException, URISyntaxException, InternalErrorException { String isoXml = null; if (isoPath != null && isAttach) { int index = isoPath.lastIndexOf("/"); String path = isoPath.substring(0, index); String name = isoPath.substring(index + 1); KVMStoragePool secondaryPool = _storagePoolMgr.getStoragePoolByURI( - path); + path); KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name); isoPath = isoVol.getPath(); @@ -3691,9 +3680,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements info.add(ram); info.add(cap); long dom0ram = Math.min(ram / 10, 768 * 1024 * 1024L);// save a maximum - // of 10% of - // system ram or - // 768M + // of 10% of + // system ram or + // 768M dom0ram = Math.max(dom0ram, _dom0MinMem); info.add(dom0ram); s_logger.debug("cpus=" + cpus + ", speed=" + speed + ", ram=" + ram @@ -4162,7 +4151,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements NodeInfo node = conn.nodeInfo(); utilization = utilization / node.cpus; if(utilization > 0){ - stats.setCPUUtilization(utilization * 100); + stats.setCPUUtilization(utilization * 100); } } diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java index 9b1afb27394..10810b70d33 100755 --- a/server/src/com/cloud/api/ApiDispatcher.java +++ b/server/src/com/cloud/api/ApiDispatcher.java @@ -84,6 +84,10 @@ public class ApiDispatcher { private static ApiDispatcher s_instance; + public static ApiDispatcher getInstance() { + return s_instance; + } + protected ApiDispatcher() { super(); Map configs = _configDao.getConfiguration(); diff --git a/server/src/com/cloud/api/commands/ListTrafficMonitorsCmd.java b/server/src/com/cloud/api/commands/ListTrafficMonitorsCmd.java index 02f51a8d002..645bf3b7307 100644 --- a/server/src/com/cloud/api/commands/ListTrafficMonitorsCmd.java +++ b/server/src/com/cloud/api/commands/ListTrafficMonitorsCmd.java @@ -19,26 +19,28 @@ package com.cloud.api.commands; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.TrafficMonitorResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.log4j.Logger; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseListCmd; -import org.apache.cloudstack.api.APICommand; -import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.api.response.ListResponse; import com.cloud.host.Host; import com.cloud.network.NetworkUsageManager; -import com.cloud.server.ManagementService; -import org.apache.cloudstack.api.response.TrafficMonitorResponse; @APICommand(name = "listTrafficMonitors", description="List traffic monitor Hosts.", responseObject = TrafficMonitorResponse.class) public class ListTrafficMonitorsCmd extends BaseListCmd { - public static final Logger s_logger = Logger.getLogger(ListServiceOfferingsCmd.class.getName()); + public static final Logger s_logger = Logger.getLogger(ListServiceOfferingsCmd.class.getName()); private static final String s_name = "listtrafficmonitorsresponse"; + @Inject NetworkUsageManager networkUsageMgr; ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @@ -66,17 +68,15 @@ public class ListTrafficMonitorsCmd extends BaseListCmd { @Override public void execute(){ - ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); - NetworkUsageManager networkUsageMgr = locator.getManager(NetworkUsageManager.class); - List trafficMonitors = networkUsageMgr.listTrafficMonitors(this); + List trafficMonitors = networkUsageMgr.listTrafficMonitors(this); ListResponse listResponse = new ListResponse(); List responses = new ArrayList(); for (Host trafficMonitor : trafficMonitors) { TrafficMonitorResponse response = networkUsageMgr.getApiResponse(trafficMonitor); - response.setObjectName("trafficmonitor"); - response.setResponseName(getCommandName()); - responses.add(response); + response.setObjectName("trafficmonitor"); + response.setResponseName(getCommandName()); + responses.add(response); } listResponse.setResponses(responses); diff --git a/server/src/com/cloud/storage/s3/S3ManagerImpl.java b/server/src/com/cloud/storage/s3/S3ManagerImpl.java index 8a1354c9282..62db0bb671d 100644 --- a/server/src/com/cloud/storage/s3/S3ManagerImpl.java +++ b/server/src/com/cloud/storage/s3/S3ManagerImpl.java @@ -42,10 +42,13 @@ import java.util.UUID; import java.util.concurrent.Callable; import javax.ejb.Local; +import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.api.command.admin.storage.AddS3Cmd; +import org.apache.cloudstack.api.command.admin.storage.ListS3sCmd; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; @@ -53,9 +56,6 @@ import com.cloud.agent.api.DeleteTemplateFromS3Command; import com.cloud.agent.api.DownloadTemplateFromS3ToSecondaryStorageCommand; import com.cloud.agent.api.UploadTemplateToS3FromSecondaryStorageCommand; import com.cloud.agent.api.to.S3TO; -import org.apache.cloudstack.api.command.admin.storage.ListS3sCmd; -import org.springframework.stereotype.Component; - import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.DataCenterVO; @@ -77,7 +77,6 @@ import com.cloud.storage.dao.VMTemplateS3Dao; import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.utils.S3Utils.ClientOptions; - import com.cloud.utils.db.Filter; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.exception.CloudRuntimeException; @@ -90,7 +89,7 @@ public class S3ManagerImpl implements S3Manager { private String name; - @Inject + @Inject private AgentManager agentManager; @Inject @@ -288,32 +287,32 @@ public class S3ManagerImpl implements S3Manager { executeWithNoWaitLock(determineLockId(accountId, templateId), new Callable() { - @Override - public Void call() throws Exception { + @Override + public Void call() throws Exception { - final Answer answer = agentManager.sendToSSVM(null, - new DeleteTemplateFromS3Command(s3, - accountId, templateId)); - if (answer == null || !answer.getResult()) { - final String errorMessage = format( - "Delete Template Failed: Unable to delete template id %1$s from S3 due to following error: %2$s", - templateId, - ((answer == null) ? "answer is null" - : answer.getDetails())); - LOGGER.error(errorMessage); - throw new CloudRuntimeException(errorMessage); - } + final Answer answer = agentManager.sendToSSVM(null, + new DeleteTemplateFromS3Command(s3, + accountId, templateId)); + if (answer == null || !answer.getResult()) { + final String errorMessage = format( + "Delete Template Failed: Unable to delete template id %1$s from S3 due to following error: %2$s", + templateId, + ((answer == null) ? "answer is null" + : answer.getDetails())); + LOGGER.error(errorMessage); + throw new CloudRuntimeException(errorMessage); + } - vmTemplateS3Dao.remove(vmTemplateS3VO.getId()); - LOGGER.debug(format( - "Deleted template %1$s from S3.", - templateId)); + vmTemplateS3Dao.remove(vmTemplateS3VO.getId()); + LOGGER.debug(format( + "Deleted template %1$s from S3.", + templateId)); - return null; + return null; - } + } - }); + }); } catch (Exception e) { @@ -384,38 +383,38 @@ public class S3ManagerImpl implements S3Manager { executeWithNoWaitLock(determineLockId(accountId, templateId), new Callable() { - @Override - public Void call() throws Exception { + @Override + public Void call() throws Exception { - final Answer answer = agentManager.sendToSSVM( - dataCenterId, cmd); + final Answer answer = agentManager.sendToSSVM( + dataCenterId, cmd); - if (answer == null || !answer.getResult()) { - final String errMsg = String - .format("Failed to download template from S3 to secondary storage due to %1$s", - (answer == null ? "answer is null" - : answer.getDetails())); - LOGGER.error(errMsg); - throw new CloudRuntimeException(errMsg); - } + if (answer == null || !answer.getResult()) { + final String errMsg = String + .format("Failed to download template from S3 to secondary storage due to %1$s", + (answer == null ? "answer is null" + : answer.getDetails())); + LOGGER.error(errMsg); + throw new CloudRuntimeException(errMsg); + } - final String installPath = join( - asList("template", "tmpl", accountId, - templateId), File.separator); - final VMTemplateHostVO tmpltHost = new VMTemplateHostVO( - secondaryStorageHost.getId(), templateId, - now(), 100, Status.DOWNLOADED, null, null, - null, installPath, template.getUrl()); - tmpltHost.setSize(templateS3VO.getSize()); - tmpltHost.setPhysicalSize(templateS3VO - .getPhysicalSize()); - vmTemplateHostDao.persist(tmpltHost); + final String installPath = join( + asList("template", "tmpl", accountId, + templateId), File.separator); + final VMTemplateHostVO tmpltHost = new VMTemplateHostVO( + secondaryStorageHost.getId(), templateId, + now(), 100, Status.DOWNLOADED, null, null, + null, installPath, template.getUrl()); + tmpltHost.setSize(templateS3VO.getSize()); + tmpltHost.setPhysicalSize(templateS3VO + .getPhysicalSize()); + vmTemplateHostDao.persist(tmpltHost); - return null; + return null; - } + } - }); + }); } catch (Exception e) { final String errMsg = "Failed to download template from S3 to secondary storage due to " @@ -608,50 +607,50 @@ public class S3ManagerImpl implements S3Manager { executeWithNoWaitLock(determineLockId(accountId, templateId), new Callable() { - @Override - public Void call() throws Exception { + @Override + public Void call() throws Exception { - final UploadTemplateToS3FromSecondaryStorageCommand cmd = new UploadTemplateToS3FromSecondaryStorageCommand( - s3, secondaryHost.getStorageUrl(), - dataCenterId, accountId, templateId); + final UploadTemplateToS3FromSecondaryStorageCommand cmd = new UploadTemplateToS3FromSecondaryStorageCommand( + s3, secondaryHost.getStorageUrl(), + dataCenterId, accountId, templateId); - final Answer answer = agentManager.sendToSSVM( - dataCenterId, cmd); - if (answer == null || !answer.getResult()) { + final Answer answer = agentManager.sendToSSVM( + dataCenterId, cmd); + if (answer == null || !answer.getResult()) { - final String reason = answer != null ? answer - .getDetails() - : "S3 template sync failed due to an unspecified error."; + final String reason = answer != null ? answer + .getDetails() + : "S3 template sync failed due to an unspecified error."; throw new CloudRuntimeException( format("Failed to upload template id %1$s to S3 from secondary storage due to %2$s.", templateId, reason)); - } + } - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(format( - "Creating VMTemplateS3VO instance using template id %1s.", - templateId)); - } + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(format( + "Creating VMTemplateS3VO instance using template id %1s.", + templateId)); + } - final VMTemplateS3VO vmTemplateS3VO = new VMTemplateS3VO( - s3.getId(), templateId, now(), - templateHostRef.getSize(), templateHostRef - .getPhysicalSize()); + final VMTemplateS3VO vmTemplateS3VO = new VMTemplateS3VO( + s3.getId(), templateId, now(), + templateHostRef.getSize(), templateHostRef + .getPhysicalSize()); - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(format("Persisting %1$s", - vmTemplateS3VO)); - } + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(format("Persisting %1$s", + vmTemplateS3VO)); + } - vmTemplateS3Dao.persist(vmTemplateS3VO); - propagateTemplateToAllZones(vmTemplateS3VO); + vmTemplateS3Dao.persist(vmTemplateS3VO); + propagateTemplateToAllZones(vmTemplateS3VO); - return null; + return null; - } + } - }); + }); } catch (Exception e) { diff --git a/usage/src/com/cloud/usage/UsageServer.java b/usage/src/com/cloud/usage/UsageServer.java index aedbe768476..5fe46e68d6d 100644 --- a/usage/src/com/cloud/usage/UsageServer.java +++ b/usage/src/com/cloud/usage/UsageServer.java @@ -38,8 +38,7 @@ public class UsageServer { } public void start() { - final ComponentLocator _locator = ComponentLocator.getLocator(UsageServer.Name, "usage-components.xml", "log4j-cloud_usage"); - UsageManager mgr = _locator.getManager(UsageManager.class); + UsageManager mgr = new UsageManager(); if (mgr != null) { if (s_logger.isInfoEnabled()) { s_logger.info("UsageServer ready..."); diff --git a/utils/test/com/cloud/utils/db/TransactionTest.java b/utils/test/com/cloud/utils/db/TransactionTest.java index 5bd1e9aca65..b952be2c28b 100644 --- a/utils/test/com/cloud/utils/db/TransactionTest.java +++ b/utils/test/com/cloud/utils/db/TransactionTest.java @@ -26,7 +26,7 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; - +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CloudRuntimeException; /** @@ -34,7 +34,7 @@ import com.cloud.utils.exception.CloudRuntimeException; * all its testcases to set up a test db table, and then tear down these test db artifacts after all testcases are run. * * @author Min Chen - * + * */ public class TransactionTest { @@ -76,7 +76,7 @@ public class TransactionTest { * that the same db connection is reused rather than acquiring a new one each time in typical transaction model. */ public void testUserManagedConnection() { - DbTestDao testDao = ComponentLocator.inject(DbTestDao.class); + DbTestDao testDao = ComponentContext.inject(DbTestDao.class); Transaction txn = Transaction.open("SingleConnectionThread"); Connection conn = null; try { @@ -115,7 +115,7 @@ public class TransactionTest { * This test is simulating ClusterHeartBeat process, where the same transaction and db connection is reused. */ public void testTransactionReuse() { - DbTestDao testDao = ComponentLocator.inject(DbTestDao.class); + DbTestDao testDao = ComponentContext.inject(DbTestDao.class); // acquire a db connection and keep it Connection conn = null; try { diff --git a/utils/test/com/cloud/utils/log/CglibThrowableRendererTest.java b/utils/test/com/cloud/utils/log/CglibThrowableRendererTest.java index 13e396db06d..bf917dead4f 100644 --- a/utils/test/com/cloud/utils/log/CglibThrowableRendererTest.java +++ b/utils/test/com/cloud/utils/log/CglibThrowableRendererTest.java @@ -20,7 +20,7 @@ import junit.framework.TestCase; import org.apache.log4j.Logger; - +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.db.DB; import com.cloud.utils.exception.CloudRuntimeException; @@ -48,9 +48,9 @@ public class CglibThrowableRendererTest extends TestCase { } } } - + public void testException() { - Test test = ComponentLocator.inject(Test.class); + Test test = ComponentContext.inject(Test.class); try { test.exception(); } catch (Exception e) { From fac227024047fd128e7f34b5c6a619afea35191f Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Thu, 10 Jan 2013 15:29:14 -0800 Subject: [PATCH 23/73] more files changed --- .../agent/manager/MockAgentManagerImpl.java | 693 +++-- .../agent/manager/MockStorageManagerImpl.java | 2256 +++++++++-------- .../agent/manager/MockVmManagerImpl.java | 483 ++-- .../agent/manager/SimulatorManagerImpl.java | 160 +- .../api/commands/ConfigureSimulator.java | 13 +- .../com/cloud/resource/AgentResourceBase.java | 373 ++- .../cloud/resource/SimulatorDiscoverer.java | 361 ++- .../SimulatorSecondaryDiscoverer.java | 137 +- .../com/cloud/simulator/SimulatorGuru.java | 12 +- .../cloud/simulator/dao/MockVMDaoImpl.java | 2 +- .../auth/SHA256SaltedUserAuthenticator.java | 146 +- .../cloud/resource/ResourceManagerImpl.java | 42 +- .../cloud/servlet/ConsoleProxyServlet.java | 12 +- .../servlet/RegisterCompleteServlet.java | 181 +- .../com/cloud/storage/StorageManagerImpl.java | 350 ++- .../storage/StorageMigrationCleanupMaid.java | 121 - ...GarbageCollectingStoragePoolAllocator.java | 68 +- .../storage/listener/StoragePoolMonitor.java | 103 +- .../DummySecondaryStorageResource.java | 128 +- server/src/com/cloud/test/DatabaseConfig.java | 1024 ++++---- server/src/com/cloud/test/IPRangeConfig.java | 906 +++---- server/src/com/cloud/test/PodZoneConfig.java | 702 ++--- .../src/com/cloud/vm/SystemVmLoadScanner.java | 100 +- .../src/com/cloud/vm/UserVmManagerImpl.java | 5 +- .../cloud/vm/VirtualMachineManagerImpl.java | 160 +- server/test/com/cloud/async/TestAsync.java | 222 +- 26 files changed, 4344 insertions(+), 4416 deletions(-) delete mode 100644 server/src/com/cloud/storage/StorageMigrationCleanupMaid.java diff --git a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockAgentManagerImpl.java b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockAgentManagerImpl.java index 506fbe02d02..0a9f93f6497 100755 --- a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockAgentManagerImpl.java +++ b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockAgentManagerImpl.java @@ -29,6 +29,7 @@ import java.util.concurrent.TimeUnit; import java.util.regex.PatternSyntaxException; import javax.ejb.Local; +import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; @@ -43,9 +44,6 @@ import com.cloud.agent.api.GetHostStatsCommand; import com.cloud.agent.api.HostStatsEntry; import com.cloud.agent.api.MaintainAnswer; import com.cloud.agent.api.PingTestCommand; -import com.cloud.agent.api.PrepareForMigrationAnswer; -import com.cloud.agent.api.PrepareForMigrationCommand; -import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.dc.dao.HostPodDao; import com.cloud.host.Host; import com.cloud.resource.AgentResourceBase; @@ -58,7 +56,6 @@ import com.cloud.simulator.MockVMVO; import com.cloud.simulator.dao.MockHostDao; import com.cloud.simulator.dao.MockVMDao; import com.cloud.utils.Pair; - import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; @@ -67,393 +64,393 @@ import com.cloud.utils.net.NetUtils; @Local(value = { MockAgentManager.class }) public class MockAgentManagerImpl implements MockAgentManager { - private static final Logger s_logger = Logger.getLogger(MockAgentManagerImpl.class); - @Inject - HostPodDao _podDao = null; - @Inject - MockHostDao _mockHostDao = null; - @Inject - MockVMDao _mockVmDao = null; - @Inject - SimulatorManager _simulatorMgr = null; - @Inject - AgentManager _agentMgr = null; - @Inject - MockStorageManager _storageMgr = null; - @Inject - ResourceManager _resourceMgr; - private SecureRandom random; - private Map _resources = new ConcurrentHashMap(); - private ThreadPoolExecutor _executor; + private static final Logger s_logger = Logger.getLogger(MockAgentManagerImpl.class); + @Inject + HostPodDao _podDao = null; + @Inject + MockHostDao _mockHostDao = null; + @Inject + MockVMDao _mockVmDao = null; + @Inject + SimulatorManager _simulatorMgr = null; + @Inject + AgentManager _agentMgr = null; + @Inject + MockStorageManager _storageMgr = null; + @Inject + ResourceManager _resourceMgr; + private SecureRandom random; + private final Map _resources = new ConcurrentHashMap(); + private ThreadPoolExecutor _executor; - private Pair getPodCidr(long podId, long dcId) { - try { + private Pair getPodCidr(long podId, long dcId) { + try { - HashMap> podMap = _podDao.getCurrentPodCidrSubnets(dcId, 0); - List cidrPair = podMap.get(podId); - String cidrAddress = (String) cidrPair.get(0); - Long cidrSize = (Long) cidrPair.get(1); - return new Pair(cidrAddress, cidrSize); - } catch (PatternSyntaxException e) { - s_logger.error("Exception while splitting pod cidr"); - return null; - } catch (IndexOutOfBoundsException e) { - s_logger.error("Invalid pod cidr. Please check"); - return null; - } - } + HashMap> podMap = _podDao.getCurrentPodCidrSubnets(dcId, 0); + List cidrPair = podMap.get(podId); + String cidrAddress = (String) cidrPair.get(0); + Long cidrSize = (Long) cidrPair.get(1); + return new Pair(cidrAddress, cidrSize); + } catch (PatternSyntaxException e) { + s_logger.error("Exception while splitting pod cidr"); + return null; + } catch (IndexOutOfBoundsException e) { + s_logger.error("Invalid pod cidr. Please check"); + return null; + } + } - private String getIpAddress(long instanceId, long dcId, long podId) { - Pair cidr = this.getPodCidr(podId, dcId); - return NetUtils.long2Ip(NetUtils.ip2Long(cidr.first()) + instanceId); - } + private String getIpAddress(long instanceId, long dcId, long podId) { + Pair cidr = this.getPodCidr(podId, dcId); + return NetUtils.long2Ip(NetUtils.ip2Long(cidr.first()) + instanceId); + } - private String getMacAddress(long dcId, long podId, long clusterId, int instanceId) { - return NetUtils.long2Mac((dcId << 40 + podId << 32 + clusterId << 24 + instanceId)); - } + private String getMacAddress(long dcId, long podId, long clusterId, int instanceId) { + return NetUtils.long2Mac((dcId << 40 + podId << 32 + clusterId << 24 + instanceId)); + } - public synchronized int getNextAgentId(long cidrSize) { - return random.nextInt((int) cidrSize); - } + public synchronized int getNextAgentId(long cidrSize) { + return random.nextInt((int) cidrSize); + } - @Override - @DB - public Map> createServerResources(Map params) { + @Override + @DB + public Map> createServerResources(Map params) { - Map args = new HashMap(); - Map> newResources = new HashMap>(); - AgentResourceBase agentResource; - long cpuCore = Long.parseLong((String) params.get("cpucore")); - long cpuSpeed = Long.parseLong((String) params.get("cpuspeed")); - long memory = Long.parseLong((String) params.get("memory")); - long localStorageSize = Long.parseLong((String) params.get("localstorage")); - synchronized (this) { - long dataCenterId = Long.parseLong((String) params.get("zone")); - long podId = Long.parseLong((String) params.get("pod")); - long clusterId = Long.parseLong((String) params.get("cluster")); - long cidrSize = getPodCidr(podId, dataCenterId).second(); + Map args = new HashMap(); + Map> newResources = new HashMap>(); + AgentResourceBase agentResource; + long cpuCore = Long.parseLong((String) params.get("cpucore")); + long cpuSpeed = Long.parseLong((String) params.get("cpuspeed")); + long memory = Long.parseLong((String) params.get("memory")); + long localStorageSize = Long.parseLong((String) params.get("localstorage")); + synchronized (this) { + long dataCenterId = Long.parseLong((String) params.get("zone")); + long podId = Long.parseLong((String) params.get("pod")); + long clusterId = Long.parseLong((String) params.get("cluster")); + long cidrSize = getPodCidr(podId, dataCenterId).second(); - int agentId = getNextAgentId(cidrSize); - String ipAddress = getIpAddress(agentId, dataCenterId, podId); - String macAddress = getMacAddress(dataCenterId, podId, clusterId, agentId); - MockHostVO mockHost = new MockHostVO(); - mockHost.setDataCenterId(dataCenterId); - mockHost.setPodId(podId); - mockHost.setClusterId(clusterId); - mockHost.setCapabilities("hvm"); - mockHost.setCpuCount(cpuCore); - mockHost.setCpuSpeed(cpuSpeed); - mockHost.setMemorySize(memory); - String guid = UUID.randomUUID().toString(); - mockHost.setGuid(guid); - mockHost.setName("SimulatedAgent." + guid); - mockHost.setPrivateIpAddress(ipAddress); - mockHost.setPublicIpAddress(ipAddress); - mockHost.setStorageIpAddress(ipAddress); - mockHost.setPrivateMacAddress(macAddress); - mockHost.setPublicMacAddress(macAddress); - mockHost.setStorageMacAddress(macAddress); - mockHost.setVersion(this.getClass().getPackage().getImplementationVersion()); - mockHost.setResource("com.cloud.agent.AgentRoutingResource"); + int agentId = getNextAgentId(cidrSize); + String ipAddress = getIpAddress(agentId, dataCenterId, podId); + String macAddress = getMacAddress(dataCenterId, podId, clusterId, agentId); + MockHostVO mockHost = new MockHostVO(); + mockHost.setDataCenterId(dataCenterId); + mockHost.setPodId(podId); + mockHost.setClusterId(clusterId); + mockHost.setCapabilities("hvm"); + mockHost.setCpuCount(cpuCore); + mockHost.setCpuSpeed(cpuSpeed); + mockHost.setMemorySize(memory); + String guid = UUID.randomUUID().toString(); + mockHost.setGuid(guid); + mockHost.setName("SimulatedAgent." + guid); + mockHost.setPrivateIpAddress(ipAddress); + mockHost.setPublicIpAddress(ipAddress); + mockHost.setStorageIpAddress(ipAddress); + mockHost.setPrivateMacAddress(macAddress); + mockHost.setPublicMacAddress(macAddress); + mockHost.setStorageMacAddress(macAddress); + mockHost.setVersion(this.getClass().getPackage().getImplementationVersion()); + mockHost.setResource("com.cloud.agent.AgentRoutingResource"); - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - mockHost = _mockHostDao.persist(mockHost); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - s_logger.error("Error while configuring mock agent " + ex.getMessage()); - throw new CloudRuntimeException("Error configuring agent", ex); - } finally { - txn.close(); + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + mockHost = _mockHostDao.persist(mockHost); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + s_logger.error("Error while configuring mock agent " + ex.getMessage()); + throw new CloudRuntimeException("Error configuring agent", ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - _storageMgr.getLocalStorage(guid, localStorageSize); + _storageMgr.getLocalStorage(guid, localStorageSize); - agentResource = new AgentRoutingResource(); - if (agentResource != null) { - try { - params.put("guid", mockHost.getGuid()); - agentResource.start(); - agentResource.configure(mockHost.getName(), params); + agentResource = new AgentRoutingResource(); + if (agentResource != null) { + try { + params.put("guid", mockHost.getGuid()); + agentResource.start(); + agentResource.configure(mockHost.getName(), params); - newResources.put(agentResource, args); - } catch (ConfigurationException e) { - s_logger.error("error while configuring server resource" + e.getMessage()); - } - } - } - return newResources; - } + newResources.put(agentResource, args); + } catch (ConfigurationException e) { + s_logger.error("error while configuring server resource" + e.getMessage()); + } + } + } + return newResources; + } - @Override - public boolean configure(String name, Map params) throws ConfigurationException { - try { - random = SecureRandom.getInstance("SHA1PRNG"); - _executor = new ThreadPoolExecutor(1, 5, 1, TimeUnit.DAYS, new LinkedBlockingQueue(), - new NamedThreadFactory("Simulator-Agent-Mgr")); - // ComponentLocator locator = ComponentLocator.getCurrentLocator(); - // _simulatorMgr = (SimulatorManager) - // locator.getComponent(SimulatorManager.Name); - } catch (NoSuchAlgorithmException e) { - s_logger.debug("Failed to initialize random:" + e.toString()); - return false; - } - return true; - } + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + try { + random = SecureRandom.getInstance("SHA1PRNG"); + _executor = new ThreadPoolExecutor(1, 5, 1, TimeUnit.DAYS, new LinkedBlockingQueue(), + new NamedThreadFactory("Simulator-Agent-Mgr")); + // ComponentLocator locator = ComponentLocator.getCurrentLocator(); + // _simulatorMgr = (SimulatorManager) + // locator.getComponent(SimulatorManager.Name); + } catch (NoSuchAlgorithmException e) { + s_logger.debug("Failed to initialize random:" + e.toString()); + return false; + } + return true; + } - @Override - public boolean handleSystemVMStart(long vmId, String privateIpAddress, String privateMacAddress, - String privateNetMask, long dcId, long podId, String name, String vmType, String url) { - _executor.execute(new SystemVMHandler(vmId, privateIpAddress, privateMacAddress, privateNetMask, dcId, podId, - name, vmType, _simulatorMgr, url)); - return true; - } + @Override + public boolean handleSystemVMStart(long vmId, String privateIpAddress, String privateMacAddress, + String privateNetMask, long dcId, long podId, String name, String vmType, String url) { + _executor.execute(new SystemVMHandler(vmId, privateIpAddress, privateMacAddress, privateNetMask, dcId, podId, + name, vmType, _simulatorMgr, url)); + return true; + } - @Override - public boolean handleSystemVMStop(long vmId) { - _executor.execute(new SystemVMHandler(vmId)); - return true; - } + @Override + public boolean handleSystemVMStop(long vmId) { + _executor.execute(new SystemVMHandler(vmId)); + return true; + } - private class SystemVMHandler implements Runnable { - private long vmId; - private String privateIpAddress; - private String privateMacAddress; - private String privateNetMask; - private long dcId; - private long podId; - private String guid; - private String name; - private String vmType; - private SimulatorManager mgr; - private String mode; - private String url; + private class SystemVMHandler implements Runnable { + private final long vmId; + private String privateIpAddress; + private String privateMacAddress; + private String privateNetMask; + private long dcId; + private long podId; + private String guid; + private String name; + private String vmType; + private SimulatorManager mgr; + private final String mode; + private String url; - public SystemVMHandler(long vmId, String privateIpAddress, String privateMacAddress, String privateNetMask, - long dcId, long podId, String name, String vmType, SimulatorManager mgr, String url) { - this.vmId = vmId; - this.privateIpAddress = privateIpAddress; - this.privateMacAddress = privateMacAddress; - this.privateNetMask = privateNetMask; - this.dcId = dcId; - this.guid = "SystemVM-" + UUID.randomUUID().toString(); - this.name = name; - this.vmType = vmType; - this.mgr = mgr; - this.mode = "Start"; - this.url = url; - this.podId = podId; - } + public SystemVMHandler(long vmId, String privateIpAddress, String privateMacAddress, String privateNetMask, + long dcId, long podId, String name, String vmType, SimulatorManager mgr, String url) { + this.vmId = vmId; + this.privateIpAddress = privateIpAddress; + this.privateMacAddress = privateMacAddress; + this.privateNetMask = privateNetMask; + this.dcId = dcId; + this.guid = "SystemVM-" + UUID.randomUUID().toString(); + this.name = name; + this.vmType = vmType; + this.mgr = mgr; + this.mode = "Start"; + this.url = url; + this.podId = podId; + } - public SystemVMHandler(long vmId) { - this.vmId = vmId; - this.mode = "Stop"; - } + public SystemVMHandler(long vmId) { + this.vmId = vmId; + this.mode = "Stop"; + } - @Override - @DB - public void run() { + @Override + @DB + public void run() { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - if (this.mode.equalsIgnoreCase("Stop")) { - txn.start(); - MockHost host = _mockHostDao.findByVmId(this.vmId); - if (host != null) { - String guid = host.getGuid(); - if (guid != null) { - AgentResourceBase res = _resources.get(guid); - if (res != null) { - res.stop(); - _resources.remove(guid); - } - } - } - txn.commit(); - return; - } - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Unable to get host " + guid + " due to " + ex.getMessage(), ex); - } finally { - txn.close(); + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + if (this.mode.equalsIgnoreCase("Stop")) { + txn.start(); + MockHost host = _mockHostDao.findByVmId(this.vmId); + if (host != null) { + String guid = host.getGuid(); + if (guid != null) { + AgentResourceBase res = _resources.get(guid); + if (res != null) { + res.stop(); + _resources.remove(guid); + } + } + } + txn.commit(); + return; + } + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Unable to get host " + guid + " due to " + ex.getMessage(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - String resource = null; - if (vmType.equalsIgnoreCase("secstorage")) { - resource = "com.cloud.agent.AgentStorageResource"; - } - MockHostVO mockHost = new MockHostVO(); - mockHost.setDataCenterId(this.dcId); - mockHost.setPodId(this.podId); - mockHost.setCpuCount(DEFAULT_HOST_CPU_CORES); - mockHost.setCpuSpeed(DEFAULT_HOST_SPEED_MHZ); - mockHost.setMemorySize(DEFAULT_HOST_MEM_SIZE); - mockHost.setGuid(this.guid); - mockHost.setName(name); - mockHost.setPrivateIpAddress(this.privateIpAddress); - mockHost.setPublicIpAddress(this.privateIpAddress); - mockHost.setStorageIpAddress(this.privateIpAddress); - mockHost.setPrivateMacAddress(this.privateMacAddress); - mockHost.setPublicMacAddress(this.privateMacAddress); - mockHost.setStorageMacAddress(this.privateMacAddress); - mockHost.setVersion(this.getClass().getPackage().getImplementationVersion()); - mockHost.setResource(resource); - mockHost.setVmId(vmId); - Transaction simtxn = Transaction.open(Transaction.SIMULATOR_DB); - try { - simtxn.start(); - mockHost = _mockHostDao.persist(mockHost); - simtxn.commit(); - } catch (Exception ex) { - simtxn.rollback(); - throw new CloudRuntimeException("Unable to persist host " + mockHost.getGuid() + " due to " - + ex.getMessage(), ex); - } finally { - simtxn.close(); + String resource = null; + if (vmType.equalsIgnoreCase("secstorage")) { + resource = "com.cloud.agent.AgentStorageResource"; + } + MockHostVO mockHost = new MockHostVO(); + mockHost.setDataCenterId(this.dcId); + mockHost.setPodId(this.podId); + mockHost.setCpuCount(DEFAULT_HOST_CPU_CORES); + mockHost.setCpuSpeed(DEFAULT_HOST_SPEED_MHZ); + mockHost.setMemorySize(DEFAULT_HOST_MEM_SIZE); + mockHost.setGuid(this.guid); + mockHost.setName(name); + mockHost.setPrivateIpAddress(this.privateIpAddress); + mockHost.setPublicIpAddress(this.privateIpAddress); + mockHost.setStorageIpAddress(this.privateIpAddress); + mockHost.setPrivateMacAddress(this.privateMacAddress); + mockHost.setPublicMacAddress(this.privateMacAddress); + mockHost.setStorageMacAddress(this.privateMacAddress); + mockHost.setVersion(this.getClass().getPackage().getImplementationVersion()); + mockHost.setResource(resource); + mockHost.setVmId(vmId); + Transaction simtxn = Transaction.open(Transaction.SIMULATOR_DB); + try { + simtxn.start(); + mockHost = _mockHostDao.persist(mockHost); + simtxn.commit(); + } catch (Exception ex) { + simtxn.rollback(); + throw new CloudRuntimeException("Unable to persist host " + mockHost.getGuid() + " due to " + + ex.getMessage(), ex); + } finally { + simtxn.close(); simtxn = Transaction.open(Transaction.CLOUD_DB); simtxn.close(); - } + } - if (vmType.equalsIgnoreCase("secstorage")) { - AgentStorageResource storageResource = new AgentStorageResource(); - try { - Map params = new HashMap(); - Map details = new HashMap(); - params.put("guid", this.guid); - details.put("guid", this.guid); - storageResource.configure("secondaryStorage", params); - storageResource.start(); - // on the simulator the ssvm is as good as a direct - // agent - _resourceMgr.addHost(mockHost.getDataCenterId(), storageResource, Host.Type.SecondaryStorageVM, - details); - _resources.put(this.guid, storageResource); - } catch (ConfigurationException e) { - s_logger.debug("Failed to load secondary storage resource: " + e.toString()); - return; - } - } - } - } + if (vmType.equalsIgnoreCase("secstorage")) { + AgentStorageResource storageResource = new AgentStorageResource(); + try { + Map params = new HashMap(); + Map details = new HashMap(); + params.put("guid", this.guid); + details.put("guid", this.guid); + storageResource.configure("secondaryStorage", params); + storageResource.start(); + // on the simulator the ssvm is as good as a direct + // agent + _resourceMgr.addHost(mockHost.getDataCenterId(), storageResource, Host.Type.SecondaryStorageVM, + details); + _resources.put(this.guid, storageResource); + } catch (ConfigurationException e) { + s_logger.debug("Failed to load secondary storage resource: " + e.toString()); + return; + } + } + } + } - @Override - public MockHost getHost(String guid) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - MockHost _host = _mockHostDao.findByGuid(guid); - txn.commit(); - if (_host != null) { - return _host; - } else { - s_logger.error("Host with guid " + guid + " was not found"); - return null; - } - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Unable to get host " + guid + " due to " + ex.getMessage(), ex); - } finally { - txn.close(); + @Override + public MockHost getHost(String guid) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + MockHost _host = _mockHostDao.findByGuid(guid); + txn.commit(); + if (_host != null) { + return _host; + } else { + s_logger.error("Host with guid " + guid + " was not found"); + return null; + } + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Unable to get host " + guid + " due to " + ex.getMessage(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } + } + } - @Override - public GetHostStatsAnswer getHostStatistic(GetHostStatsCommand cmd) { - String hostGuid = cmd.getHostGuid(); - MockHost host = null; - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - host = _mockHostDao.findByGuid(hostGuid); - txn.commit(); - if (host == null) { - return null; - } - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Unable to get host " + hostGuid + " due to " + ex.getMessage(), ex); - } finally { - txn.close(); + @Override + public GetHostStatsAnswer getHostStatistic(GetHostStatsCommand cmd) { + String hostGuid = cmd.getHostGuid(); + MockHost host = null; + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + host = _mockHostDao.findByGuid(hostGuid); + txn.commit(); + if (host == null) { + return null; + } + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Unable to get host " + hostGuid + " due to " + ex.getMessage(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - Transaction vmtxn = Transaction.open(Transaction.SIMULATOR_DB); - try { - vmtxn.start(); - List vms = _mockVmDao.findByHostId(host.getId()); - vmtxn.commit(); - double usedMem = 0.0; - double usedCpu = 0.0; - for (MockVMVO vm : vms) { - usedMem += vm.getMemory(); - usedCpu += vm.getCpu(); - } + Transaction vmtxn = Transaction.open(Transaction.SIMULATOR_DB); + try { + vmtxn.start(); + List vms = _mockVmDao.findByHostId(host.getId()); + vmtxn.commit(); + double usedMem = 0.0; + double usedCpu = 0.0; + for (MockVMVO vm : vms) { + usedMem += vm.getMemory(); + usedCpu += vm.getCpu(); + } - HostStatsEntry hostStats = new HostStatsEntry(); - hostStats.setTotalMemoryKBs(host.getMemorySize()); - hostStats.setFreeMemoryKBs(host.getMemorySize() - usedMem); - hostStats.setNetworkReadKBs(32768); - hostStats.setNetworkWriteKBs(16384); - hostStats.setCpuUtilization(usedCpu / (host.getCpuCount() * host.getCpuSpeed())); - hostStats.setEntityType("simulator-host"); - hostStats.setHostId(cmd.getHostId()); - return new GetHostStatsAnswer(cmd, hostStats); - } catch (Exception ex) { - vmtxn.rollback(); - throw new CloudRuntimeException("Unable to get Vms on host " + host.getGuid() + " due to " - + ex.getMessage(), ex); - } finally { - vmtxn.close(); + HostStatsEntry hostStats = new HostStatsEntry(); + hostStats.setTotalMemoryKBs(host.getMemorySize()); + hostStats.setFreeMemoryKBs(host.getMemorySize() - usedMem); + hostStats.setNetworkReadKBs(32768); + hostStats.setNetworkWriteKBs(16384); + hostStats.setCpuUtilization(usedCpu / (host.getCpuCount() * host.getCpuSpeed())); + hostStats.setEntityType("simulator-host"); + hostStats.setHostId(cmd.getHostId()); + return new GetHostStatsAnswer(cmd, hostStats); + } catch (Exception ex) { + vmtxn.rollback(); + throw new CloudRuntimeException("Unable to get Vms on host " + host.getGuid() + " due to " + + ex.getMessage(), ex); + } finally { + vmtxn.close(); vmtxn = Transaction.open(Transaction.CLOUD_DB); vmtxn.close(); - } - } + } + } - @Override - public Answer checkHealth(CheckHealthCommand cmd) { - return new Answer(cmd); - } + @Override + public Answer checkHealth(CheckHealthCommand cmd) { + return new Answer(cmd); + } - @Override - public Answer pingTest(PingTestCommand cmd) { - return new Answer(cmd); - } + @Override + public Answer pingTest(PingTestCommand cmd) { + return new Answer(cmd); + } - @Override - public boolean start() { - return true; - } + @Override + public boolean start() { + return true; + } - @Override - public boolean stop() { - return true; - } + @Override + public boolean stop() { + return true; + } - @Override - public String getName() { - return this.getClass().getSimpleName(); - } + @Override + public String getName() { + return this.getClass().getSimpleName(); + } - @Override - public MaintainAnswer maintain(com.cloud.agent.api.MaintainCommand cmd) { - return new MaintainAnswer(cmd); - } + @Override + public MaintainAnswer maintain(com.cloud.agent.api.MaintainCommand cmd) { + return new MaintainAnswer(cmd); + } - @Override - public Answer checkNetworkCommand(CheckNetworkCommand cmd) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Checking if network name setup is done on the resource"); - } - return new CheckNetworkAnswer(cmd, true, "Network Setup check by names is done"); - } + @Override + public Answer checkNetworkCommand(CheckNetworkCommand cmd) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Checking if network name setup is done on the resource"); + } + return new CheckNetworkAnswer(cmd, true, "Network Setup check by names is done"); + } } diff --git a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockStorageManagerImpl.java b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockStorageManagerImpl.java index f13925440c8..3c371bc4363 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockStorageManagerImpl.java +++ b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockStorageManagerImpl.java @@ -28,9 +28,9 @@ import java.util.Map; import java.util.UUID; import javax.ejb.Local; +import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.agent.api.storage.*; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; @@ -57,6 +57,22 @@ import com.cloud.agent.api.SecStorageSetupAnswer; import com.cloud.agent.api.SecStorageSetupCommand; import com.cloud.agent.api.SecStorageVMSetupCommand; import com.cloud.agent.api.StoragePoolInfo; +import com.cloud.agent.api.storage.CopyVolumeAnswer; +import com.cloud.agent.api.storage.CopyVolumeCommand; +import com.cloud.agent.api.storage.CreateAnswer; +import com.cloud.agent.api.storage.CreateCommand; +import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; +import com.cloud.agent.api.storage.DeleteTemplateCommand; +import com.cloud.agent.api.storage.DestroyCommand; +import com.cloud.agent.api.storage.DownloadAnswer; +import com.cloud.agent.api.storage.DownloadCommand; +import com.cloud.agent.api.storage.DownloadProgressCommand; +import com.cloud.agent.api.storage.ListTemplateAnswer; +import com.cloud.agent.api.storage.ListTemplateCommand; +import com.cloud.agent.api.storage.ListVolumeAnswer; +import com.cloud.agent.api.storage.ListVolumeCommand; +import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; +import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.agent.api.to.VolumeTO; import com.cloud.simulator.MockHost; @@ -71,13 +87,11 @@ import com.cloud.simulator.dao.MockSecStorageDao; import com.cloud.simulator.dao.MockStoragePoolDao; import com.cloud.simulator.dao.MockVMDao; import com.cloud.simulator.dao.MockVolumeDao; -import com.cloud.storage.Storage; -import com.cloud.storage.VMTemplateStorageResourceAssoc; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.storage.VMTemplateStorageResourceAssoc; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; import com.cloud.storage.template.TemplateInfo; - import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.DiskProfile; @@ -85,1244 +99,1244 @@ import com.cloud.vm.VirtualMachine.State; @Local(value = { MockStorageManager.class }) public class MockStorageManagerImpl implements MockStorageManager { - private static final Logger s_logger = Logger.getLogger(MockStorageManagerImpl.class); - @Inject - MockStoragePoolDao _mockStoragePoolDao = null; - @Inject - MockSecStorageDao _mockSecStorageDao = null; - @Inject - MockVolumeDao _mockVolumeDao = null; - @Inject - MockVMDao _mockVMDao = null; - @Inject - MockHostDao _mockHostDao = null; + private static final Logger s_logger = Logger.getLogger(MockStorageManagerImpl.class); + @Inject + MockStoragePoolDao _mockStoragePoolDao = null; + @Inject + MockSecStorageDao _mockSecStorageDao = null; + @Inject + MockVolumeDao _mockVolumeDao = null; + @Inject + MockVMDao _mockVMDao = null; + @Inject + MockHostDao _mockHostDao = null; - private MockVolumeVO findVolumeFromSecondary(String path, String ssUrl, MockVolumeType type) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - String volumePath = path.replaceAll(ssUrl, ""); - MockSecStorageVO secStorage = _mockSecStorageDao.findByUrl(ssUrl); - if (secStorage == null) { - return null; - } - volumePath = secStorage.getMountPoint() + volumePath; - volumePath = volumePath.replaceAll("//", "/"); - MockVolumeVO volume = _mockVolumeDao.findByStoragePathAndType(volumePath); - txn.commit(); - if (volume == null) { - return null; - } - return volume; - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Unable to find volume " + path + " on secondary " + ssUrl, ex); - } finally { - txn.close(); + private MockVolumeVO findVolumeFromSecondary(String path, String ssUrl, MockVolumeType type) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + String volumePath = path.replaceAll(ssUrl, ""); + MockSecStorageVO secStorage = _mockSecStorageDao.findByUrl(ssUrl); + if (secStorage == null) { + return null; + } + volumePath = secStorage.getMountPoint() + volumePath; + volumePath = volumePath.replaceAll("//", "/"); + MockVolumeVO volume = _mockVolumeDao.findByStoragePathAndType(volumePath); + txn.commit(); + if (volume == null) { + return null; + } + return volume; + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Unable to find volume " + path + " on secondary " + ssUrl, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } - - @Override - public PrimaryStorageDownloadAnswer primaryStorageDownload(PrimaryStorageDownloadCommand cmd) { - MockVolumeVO template = findVolumeFromSecondary(cmd.getUrl(), cmd.getSecondaryStorageUrl(), - MockVolumeType.TEMPLATE); - if (template == null) { - return new PrimaryStorageDownloadAnswer("Can't find primary storage"); - } - - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockStoragePoolVO primaryStorage = null; - try { - txn.start(); - primaryStorage = _mockStoragePoolDao.findByUuid(cmd.getPoolUuid()); - txn.commit(); - if (primaryStorage == null) { - return new PrimaryStorageDownloadAnswer("Can't find primary storage"); - } - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when finding primary storagee " + cmd.getPoolUuid(), ex); - } finally { - txn.close(); - txn = Transaction.open(Transaction.CLOUD_DB); - txn.close(); - } - - String volumeName = UUID.randomUUID().toString(); - MockVolumeVO newVolume = new MockVolumeVO(); - newVolume.setName(volumeName); - newVolume.setPath(primaryStorage.getMountPoint() + volumeName); - newVolume.setPoolId(primaryStorage.getId()); - newVolume.setSize(template.getSize()); - newVolume.setType(MockVolumeType.VOLUME); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - _mockVolumeDao.persist(newVolume); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when saving volume " + newVolume, ex); - } finally { - txn.close(); - txn = Transaction.open(Transaction.CLOUD_DB); - txn.close(); - } - return new PrimaryStorageDownloadAnswer(newVolume.getPath(), newVolume.getSize()); - } - - @Override - public CreateAnswer createVolume(CreateCommand cmd) { - StorageFilerTO sf = cmd.getPool(); - DiskProfile dskch = cmd.getDiskCharacteristics(); - MockStoragePoolVO storagePool = null; - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - storagePool = _mockStoragePoolDao.findByUuid(sf.getUuid()); - txn.commit(); - if (storagePool == null) { - return new CreateAnswer(cmd, "Failed to find storage pool: " + sf.getUuid()); - } - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when finding storage " + sf.getUuid(), ex); - } finally { - txn.close(); - txn = Transaction.open(Transaction.CLOUD_DB); - txn.close(); - } - - String volumeName = UUID.randomUUID().toString(); - MockVolumeVO volume = new MockVolumeVO(); - volume.setPoolId(storagePool.getId()); - volume.setName(volumeName); - volume.setPath(storagePool.getMountPoint() + volumeName); - volume.setSize(dskch.getSize()); - volume.setType(MockVolumeType.VOLUME); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - volume = _mockVolumeDao.persist(volume); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when saving volume " + volume, ex); - } finally { - txn.close(); - txn = Transaction.open(Transaction.CLOUD_DB); - txn.close(); - } - - VolumeTO volumeTo = new VolumeTO(cmd.getVolumeId(), dskch.getType(), sf.getType(), sf.getUuid(), - volume.getName(), storagePool.getMountPoint(), volume.getPath(), volume.getSize(), null); - - return new CreateAnswer(cmd, volumeTo); - } - - @Override - public AttachVolumeAnswer AttachVolume(AttachVolumeCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - String poolid = cmd.getPoolUuid(); - String volumeName = cmd.getVolumeName(); - MockVolumeVO volume = _mockVolumeDao.findByStoragePathAndType(cmd.getVolumePath()); - if (volume == null) { - return new AttachVolumeAnswer(cmd, "Can't find volume:" + volumeName + "on pool:" + poolid); - } - - String vmName = cmd.getVmName(); - MockVMVO vm = _mockVMDao.findByVmName(vmName); - if (vm == null) { - return new AttachVolumeAnswer(cmd, "can't vm :" + vmName); - } - txn.commit(); - - return new AttachVolumeAnswer(cmd, cmd.getDeviceId()); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when attaching volume " + cmd.getVolumeName() + " to VM " - + cmd.getVmName(), ex); - } finally { - txn.close(); - txn = Transaction.open(Transaction.CLOUD_DB); - txn.close(); - } - } - - @Override - public Answer AttachIso(AttachIsoCommand cmd) { - MockVolumeVO iso = findVolumeFromSecondary(cmd.getIsoPath(), cmd.getStoreUrl(), MockVolumeType.ISO); - if (iso == null) { - return new Answer(cmd, false, "Failed to find the iso: " + cmd.getIsoPath() + "on secondary storage " - + cmd.getStoreUrl()); - } - - String vmName = cmd.getVmName(); - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockVMVO vm = null; - try { - txn.start(); - vm = _mockVMDao.findByVmName(vmName); - txn.commit(); - if (vm == null) { - return new Answer(cmd, false, "can't vm :" + vmName); - } - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when attaching iso to vm " + vm.getName(), ex); - } finally { - txn.close(); - txn = Transaction.open(Transaction.CLOUD_DB); - txn.close(); - } - return new Answer(cmd); - } - - @Override - public Answer DeleteStoragePool(DeleteStoragePoolCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - MockStoragePoolVO storage = _mockStoragePoolDao.findByUuid(cmd.getPool().getUuid()); - if (storage == null) { - return new Answer(cmd, false, "can't find storage pool:" + cmd.getPool().getUuid()); - } - _mockStoragePoolDao.remove(storage.getId()); - txn.commit(); - return new Answer(cmd); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when deleting storage pool " + cmd.getPool().getPath(), ex); - } finally { - txn.close(); - txn = Transaction.open(Transaction.CLOUD_DB); - txn.close(); - } - } - - @Override - public ModifyStoragePoolAnswer ModifyStoragePool(ModifyStoragePoolCommand cmd) { - StorageFilerTO sf = cmd.getPool(); - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockStoragePoolVO storagePool = null; - try { - txn.start(); - storagePool = _mockStoragePoolDao.findByUuid(sf.getUuid()); - if (storagePool == null) { - storagePool = new MockStoragePoolVO(); - storagePool.setUuid(sf.getUuid()); - storagePool.setMountPoint("/mnt/" + sf.getUuid() + File.separator); - - Long size = DEFAULT_HOST_STORAGE_SIZE; - String path = sf.getPath(); - int index = path.lastIndexOf("/"); - if (index != -1) { - path = path.substring(index + 1); - if (path != null) { - String values[] = path.split("="); - if (values.length > 1 && values[0].equalsIgnoreCase("size")) { - size = Long.parseLong(values[1]); - } - } - } - storagePool.setCapacity(size); - storagePool.setStorageType(sf.getType()); - storagePool = _mockStoragePoolDao.persist(storagePool); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when modifying storage pool " + cmd.getPool().getPath(), ex); - } finally { - txn.close(); - txn = Transaction.open(Transaction.CLOUD_DB); - txn.close(); - } - return new ModifyStoragePoolAnswer(cmd, storagePool.getCapacity(), 0, new HashMap()); - } - - @Override - public Answer CreateStoragePool(CreateStoragePoolCommand cmd) { - StorageFilerTO sf = cmd.getPool(); - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockStoragePoolVO storagePool = null; - try { - txn.start(); - storagePool = _mockStoragePoolDao.findByUuid(sf.getUuid()); - if (storagePool == null) { - storagePool = new MockStoragePoolVO(); - storagePool.setUuid(sf.getUuid()); - storagePool.setMountPoint("/mnt/" + sf.getUuid() + File.separator); - - Long size = DEFAULT_HOST_STORAGE_SIZE; - String path = sf.getPath(); - int index = path.lastIndexOf("/"); - if (index != -1) { - path = path.substring(index + 1); - if (path != null) { - String values[] = path.split("="); - if (values.length > 1 && values[0].equalsIgnoreCase("size")) { - size = Long.parseLong(values[1]); - } - } - } - storagePool.setCapacity(size); - storagePool.setStorageType(sf.getType()); - storagePool = _mockStoragePoolDao.persist(storagePool); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when creating storage pool " + cmd.getPool().getPath(), ex); - } finally { - txn.close(); - txn = Transaction.open(Transaction.CLOUD_DB); - txn.close(); - } - return new ModifyStoragePoolAnswer(cmd, storagePool.getCapacity(), 0, new HashMap()); - } - - @Override - public Answer SecStorageSetup(SecStorageSetupCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockSecStorageVO storage = null; - try { - txn.start(); - storage = _mockSecStorageDao.findByUrl(cmd.getSecUrl()); - if (storage == null) { - return new Answer(cmd, false, "can't find the storage"); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when setting up sec storage" + cmd.getSecUrl(), ex); - } finally { - txn.close(); - txn = Transaction.open(Transaction.CLOUD_DB); - txn.close(); - } - return new SecStorageSetupAnswer(storage.getMountPoint()); - } + } + } @Override - public Answer ListVolumes(ListVolumeCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockSecStorageVO storage = null; - try { - txn.start(); - storage = _mockSecStorageDao.findByUrl(cmd.getSecUrl()); - if (storage == null) { - return new Answer(cmd, false, "Failed to get secondary storage"); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when finding sec storage " + cmd.getSecUrl(), ex); - } finally { - txn.close(); + public PrimaryStorageDownloadAnswer primaryStorageDownload(PrimaryStorageDownloadCommand cmd) { + MockVolumeVO template = findVolumeFromSecondary(cmd.getUrl(), cmd.getSecondaryStorageUrl(), + MockVolumeType.TEMPLATE); + if (template == null) { + return new PrimaryStorageDownloadAnswer("Can't find primary storage"); + } + + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockStoragePoolVO primaryStorage = null; + try { + txn.start(); + primaryStorage = _mockStoragePoolDao.findByUuid(cmd.getPoolUuid()); + txn.commit(); + if (primaryStorage == null) { + return new PrimaryStorageDownloadAnswer("Can't find primary storage"); + } + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when finding primary storagee " + cmd.getPoolUuid(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - List volumes = _mockVolumeDao.findByStorageIdAndType(storage.getId(), - MockVolumeType.VOLUME); - - Map templateInfos = new HashMap(); - for (MockVolumeVO volume : volumes) { - templateInfos.put(volume.getId(), new TemplateInfo(volume.getName(), volume.getPath() - .replaceAll(storage.getMountPoint(), ""), volume.getSize(), volume.getSize(), true, false)); - } - txn.commit(); - return new ListVolumeAnswer(cmd.getSecUrl(), templateInfos); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when finding template on sec storage " + storage.getId(), ex); - } finally { - txn.close(); + String volumeName = UUID.randomUUID().toString(); + MockVolumeVO newVolume = new MockVolumeVO(); + newVolume.setName(volumeName); + newVolume.setPath(primaryStorage.getMountPoint() + volumeName); + newVolume.setPoolId(primaryStorage.getId()); + newVolume.setSize(template.getSize()); + newVolume.setType(MockVolumeType.VOLUME); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + _mockVolumeDao.persist(newVolume); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when saving volume " + newVolume, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } + } + return new PrimaryStorageDownloadAnswer(newVolume.getPath(), newVolume.getSize()); + } - @Override - public Answer ListTemplates(ListTemplateCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockSecStorageVO storage = null; - try { - txn.start(); - storage = _mockSecStorageDao.findByUrl(cmd.getSecUrl()); - if (storage == null) { - return new Answer(cmd, false, "Failed to get secondary storage"); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when finding sec storage " + cmd.getSecUrl(), ex); - } finally { - txn.close(); + @Override + public CreateAnswer createVolume(CreateCommand cmd) { + StorageFilerTO sf = cmd.getPool(); + DiskProfile dskch = cmd.getDiskCharacteristics(); + MockStoragePoolVO storagePool = null; + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + storagePool = _mockStoragePoolDao.findByUuid(sf.getUuid()); + txn.commit(); + if (storagePool == null) { + return new CreateAnswer(cmd, "Failed to find storage pool: " + sf.getUuid()); + } + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when finding storage " + sf.getUuid(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - List templates = _mockVolumeDao.findByStorageIdAndType(storage.getId(), - MockVolumeType.TEMPLATE); - - Map templateInfos = new HashMap(); - for (MockVolumeVO template : templates) { - templateInfos.put(template.getName(), new TemplateInfo(template.getName(), template.getPath() - .replaceAll(storage.getMountPoint(), ""), template.getSize(), template.getSize(), true, false)); - } - txn.commit(); - return new ListTemplateAnswer(cmd.getSecUrl(), templateInfos); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when finding template on sec storage " + storage.getId(), ex); - } finally { - txn.close(); + String volumeName = UUID.randomUUID().toString(); + MockVolumeVO volume = new MockVolumeVO(); + volume.setPoolId(storagePool.getId()); + volume.setName(volumeName); + volume.setPath(storagePool.getMountPoint() + volumeName); + volume.setSize(dskch.getSize()); + volume.setType(MockVolumeType.VOLUME); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + volume = _mockVolumeDao.persist(volume); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when saving volume " + volume, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } + } - @Override - public Answer Destroy(DestroyCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - MockVolumeVO volume = _mockVolumeDao.findByStoragePathAndType(cmd.getVolume().getPath()); - if (volume != null) { - _mockVolumeDao.remove(volume.getId()); - } + VolumeTO volumeTo = new VolumeTO(cmd.getVolumeId(), dskch.getType(), sf.getType(), sf.getUuid(), + volume.getName(), storagePool.getMountPoint(), volume.getPath(), volume.getSize(), null); - if (cmd.getVmName() != null) { - MockVm vm = _mockVMDao.findByVmName(cmd.getVmName()); - vm.setState(State.Expunging); - if (vm != null) { - MockVMVO vmVo = _mockVMDao.createForUpdate(vm.getId()); - _mockVMDao.update(vm.getId(), vmVo); - } - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when destroying volume " + cmd.getVolume().getPath(), ex); - } finally { - txn.close(); + return new CreateAnswer(cmd, volumeTo); + } + + @Override + public AttachVolumeAnswer AttachVolume(AttachVolumeCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + String poolid = cmd.getPoolUuid(); + String volumeName = cmd.getVolumeName(); + MockVolumeVO volume = _mockVolumeDao.findByStoragePathAndType(cmd.getVolumePath()); + if (volume == null) { + return new AttachVolumeAnswer(cmd, "Can't find volume:" + volumeName + "on pool:" + poolid); + } + + String vmName = cmd.getVmName(); + MockVMVO vm = _mockVMDao.findByVmName(vmName); + if (vm == null) { + return new AttachVolumeAnswer(cmd, "can't vm :" + vmName); + } + txn.commit(); + + return new AttachVolumeAnswer(cmd, cmd.getDeviceId()); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when attaching volume " + cmd.getVolumeName() + " to VM " + + cmd.getVmName(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - return new Answer(cmd); - } + } + } - @Override - public DownloadAnswer Download(DownloadCommand cmd) { - MockSecStorageVO ssvo = null; - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - ssvo = _mockSecStorageDao.findByUrl(cmd.getSecUrl()); - if (ssvo == null) { - return new DownloadAnswer("can't find secondary storage", - VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error accessing secondary storage " + cmd.getSecUrl(), ex); - } finally { - txn.close(); + @Override + public Answer AttachIso(AttachIsoCommand cmd) { + MockVolumeVO iso = findVolumeFromSecondary(cmd.getIsoPath(), cmd.getStoreUrl(), MockVolumeType.ISO); + if (iso == null) { + return new Answer(cmd, false, "Failed to find the iso: " + cmd.getIsoPath() + "on secondary storage " + + cmd.getStoreUrl()); + } + + String vmName = cmd.getVmName(); + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockVMVO vm = null; + try { + txn.start(); + vm = _mockVMDao.findByVmName(vmName); + txn.commit(); + if (vm == null) { + return new Answer(cmd, false, "can't vm :" + vmName); + } + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when attaching iso to vm " + vm.getName(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } + return new Answer(cmd); + } - MockVolumeVO volume = new MockVolumeVO(); - volume.setPoolId(ssvo.getId()); - volume.setName(cmd.getName()); - volume.setPath(ssvo.getMountPoint() + cmd.getName()); - volume.setSize(0); - volume.setType(MockVolumeType.TEMPLATE); - volume.setStatus(Status.DOWNLOAD_IN_PROGRESS); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - volume = _mockVolumeDao.persist(volume); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when saving volume " + volume, ex); - } finally { - txn.close(); + @Override + public Answer DeleteStoragePool(DeleteStoragePoolCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + MockStoragePoolVO storage = _mockStoragePoolDao.findByUuid(cmd.getPool().getUuid()); + if (storage == null) { + return new Answer(cmd, false, "can't find storage pool:" + cmd.getPool().getUuid()); + } + _mockStoragePoolDao.remove(storage.getId()); + txn.commit(); + return new Answer(cmd); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when deleting storage pool " + cmd.getPool().getPath(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - return new DownloadAnswer(String.valueOf(volume.getId()), 0, "Downloading", Status.DOWNLOAD_IN_PROGRESS, - cmd.getName(), cmd.getName(), volume.getSize(), volume.getSize(), null); - } + } + } - @Override - public DownloadAnswer DownloadProcess(DownloadProgressCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - String volumeId = cmd.getJobId(); - MockVolumeVO volume = _mockVolumeDao.findById(Long.parseLong(volumeId)); - if (volume == null) { - return new DownloadAnswer("Can't find the downloading volume", Status.ABANDONED); - } + @Override + public ModifyStoragePoolAnswer ModifyStoragePool(ModifyStoragePoolCommand cmd) { + StorageFilerTO sf = cmd.getPool(); + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockStoragePoolVO storagePool = null; + try { + txn.start(); + storagePool = _mockStoragePoolDao.findByUuid(sf.getUuid()); + if (storagePool == null) { + storagePool = new MockStoragePoolVO(); + storagePool.setUuid(sf.getUuid()); + storagePool.setMountPoint("/mnt/" + sf.getUuid() + File.separator); - long size = Math.min(volume.getSize() + DEFAULT_TEMPLATE_SIZE / 5, DEFAULT_TEMPLATE_SIZE); - volume.setSize(size); - - double volumeSize = volume.getSize(); - double pct = volumeSize / DEFAULT_TEMPLATE_SIZE; - if (pct >= 1.0) { - volume.setStatus(Status.DOWNLOADED); - _mockVolumeDao.update(volume.getId(), volume); - txn.commit(); - return new DownloadAnswer(cmd.getJobId(), 100, cmd, - com.cloud.storage.VMTemplateStorageResourceAssoc.Status.DOWNLOADED, volume.getPath(), - volume.getName()); - } else { - _mockVolumeDao.update(volume.getId(), volume); - txn.commit(); - return new DownloadAnswer(cmd.getJobId(), (int) (pct * 100.0), cmd, - com.cloud.storage.VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS, volume.getPath(), - volume.getName()); - } - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error during download job " + cmd.getJobId(), ex); - } finally { - txn.close(); + Long size = DEFAULT_HOST_STORAGE_SIZE; + String path = sf.getPath(); + int index = path.lastIndexOf("/"); + if (index != -1) { + path = path.substring(index + 1); + if (path != null) { + String values[] = path.split("="); + if (values.length > 1 && values[0].equalsIgnoreCase("size")) { + size = Long.parseLong(values[1]); + } + } + } + storagePool.setCapacity(size); + storagePool.setStorageType(sf.getType()); + storagePool = _mockStoragePoolDao.persist(storagePool); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when modifying storage pool " + cmd.getPool().getPath(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } + } + return new ModifyStoragePoolAnswer(cmd, storagePool.getCapacity(), 0, new HashMap()); + } - @Override - public GetStorageStatsAnswer GetStorageStats(GetStorageStatsCommand cmd) { - String uuid = cmd.getStorageId(); - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - if (uuid == null) { - String secUrl = cmd.getSecUrl(); - MockSecStorageVO secondary = _mockSecStorageDao.findByUrl(secUrl); - if (secondary == null) { - return new GetStorageStatsAnswer(cmd, "Can't find the secondary storage:" + secUrl); - } - Long totalUsed = _mockVolumeDao.findTotalStorageId(secondary.getId()); - txn.commit(); - return new GetStorageStatsAnswer(cmd, secondary.getCapacity(), totalUsed); - } else { - MockStoragePoolVO pool = _mockStoragePoolDao.findByUuid(uuid); - if (pool == null) { - return new GetStorageStatsAnswer(cmd, "Can't find the pool"); - } - Long totalUsed = _mockVolumeDao.findTotalStorageId(pool.getId()); - if (totalUsed == null) { - totalUsed = 0L; - } - txn.commit(); - return new GetStorageStatsAnswer(cmd, pool.getCapacity(), totalUsed); - } - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("DBException during storage stats collection for pool " + uuid, ex); - } finally { - txn.close(); + @Override + public Answer CreateStoragePool(CreateStoragePoolCommand cmd) { + StorageFilerTO sf = cmd.getPool(); + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockStoragePoolVO storagePool = null; + try { + txn.start(); + storagePool = _mockStoragePoolDao.findByUuid(sf.getUuid()); + if (storagePool == null) { + storagePool = new MockStoragePoolVO(); + storagePool.setUuid(sf.getUuid()); + storagePool.setMountPoint("/mnt/" + sf.getUuid() + File.separator); + + Long size = DEFAULT_HOST_STORAGE_SIZE; + String path = sf.getPath(); + int index = path.lastIndexOf("/"); + if (index != -1) { + path = path.substring(index + 1); + if (path != null) { + String values[] = path.split("="); + if (values.length > 1 && values[0].equalsIgnoreCase("size")) { + size = Long.parseLong(values[1]); + } + } + } + storagePool.setCapacity(size); + storagePool.setStorageType(sf.getType()); + storagePool = _mockStoragePoolDao.persist(storagePool); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when creating storage pool " + cmd.getPool().getPath(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } + } + return new ModifyStoragePoolAnswer(cmd, storagePool.getCapacity(), 0, new HashMap()); + } - @Override - public ManageSnapshotAnswer ManageSnapshot(ManageSnapshotCommand cmd) { - String volPath = cmd.getVolumePath(); - MockVolumeVO volume = null; - MockStoragePoolVO storagePool = null; - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - volume = _mockVolumeDao.findByStoragePathAndType(volPath); - if (volume == null) { - return new ManageSnapshotAnswer(cmd, false, "Can't find the volume"); - } - storagePool = _mockStoragePoolDao.findById(volume.getPoolId()); - if (storagePool == null) { - return new ManageSnapshotAnswer(cmd, false, "Can't find the storage pooll"); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Unable to perform snapshot", ex); - } finally { - txn.close(); + @Override + public Answer SecStorageSetup(SecStorageSetupCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockSecStorageVO storage = null; + try { + txn.start(); + storage = _mockSecStorageDao.findByUrl(cmd.getSecUrl()); + if (storage == null) { + return new Answer(cmd, false, "can't find the storage"); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when setting up sec storage" + cmd.getSecUrl(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } + return new SecStorageSetupAnswer(storage.getMountPoint()); + } - String mountPoint = storagePool.getMountPoint(); - MockVolumeVO snapshot = new MockVolumeVO(); - - snapshot.setName(cmd.getSnapshotName()); - snapshot.setPath(mountPoint + cmd.getSnapshotName()); - snapshot.setSize(volume.getSize()); - snapshot.setPoolId(storagePool.getId()); - snapshot.setType(MockVolumeType.SNAPSHOT); - snapshot.setStatus(Status.DOWNLOADED); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - snapshot = _mockVolumeDao.persist(snapshot); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when saving snapshot " + snapshot, ex); - } finally { - txn.close(); + @Override + public Answer ListVolumes(ListVolumeCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockSecStorageVO storage = null; + try { + txn.start(); + storage = _mockSecStorageDao.findByUrl(cmd.getSecUrl()); + if (storage == null) { + return new Answer(cmd, false, "Failed to get secondary storage"); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when finding sec storage " + cmd.getSecUrl(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - return new ManageSnapshotAnswer(cmd, snapshot.getId(), snapshot.getPath(), true, ""); - } + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + List volumes = _mockVolumeDao.findByStorageIdAndType(storage.getId(), + MockVolumeType.VOLUME); - @Override - public BackupSnapshotAnswer BackupSnapshot(BackupSnapshotCommand cmd, SimulatorInfo info) { - // emulate xenserver backupsnapshot, if the base volume is deleted, then - // backupsnapshot failed - MockVolumeVO volume = null; - MockVolumeVO snapshot = null; - MockSecStorageVO secStorage = null; - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - volume = _mockVolumeDao.findByStoragePathAndType(cmd.getVolumePath()); - if (volume == null) { - return new BackupSnapshotAnswer(cmd, false, "Can't find base volume: " + cmd.getVolumePath(), null, - true); - } - String snapshotPath = cmd.getSnapshotUuid(); - snapshot = _mockVolumeDao.findByStoragePathAndType(snapshotPath); - if (snapshot == null) { - return new BackupSnapshotAnswer(cmd, false, "can't find snapshot" + snapshotPath, null, true); - } - - String secStorageUrl = cmd.getSecondaryStorageUrl(); - secStorage = _mockSecStorageDao.findByUrl(secStorageUrl); - if (secStorage == null) { - return new BackupSnapshotAnswer(cmd, false, "can't find sec storage" + snapshotPath, null, true); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when backing up snapshot"); - } finally { - txn.close(); + Map templateInfos = new HashMap(); + for (MockVolumeVO volume : volumes) { + templateInfos.put(volume.getId(), new TemplateInfo(volume.getName(), volume.getPath() + .replaceAll(storage.getMountPoint(), ""), volume.getSize(), volume.getSize(), true, false)); + } + txn.commit(); + return new ListVolumeAnswer(cmd.getSecUrl(), templateInfos); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when finding template on sec storage " + storage.getId(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } + } - MockVolumeVO newsnapshot = new MockVolumeVO(); - String name = UUID.randomUUID().toString(); - newsnapshot.setName(name); - newsnapshot.setPath(secStorage.getMountPoint() + name); - newsnapshot.setPoolId(secStorage.getId()); - newsnapshot.setSize(snapshot.getSize()); - newsnapshot.setStatus(Status.DOWNLOADED); - newsnapshot.setType(MockVolumeType.SNAPSHOT); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - snapshot = _mockVolumeDao.persist(snapshot); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when backing up snapshot " + newsnapshot, ex); - } finally { - txn.close(); + @Override + public Answer ListTemplates(ListTemplateCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockSecStorageVO storage = null; + try { + txn.start(); + storage = _mockSecStorageDao.findByUrl(cmd.getSecUrl()); + if (storage == null) { + return new Answer(cmd, false, "Failed to get secondary storage"); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when finding sec storage " + cmd.getSecUrl(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - return new BackupSnapshotAnswer(cmd, true, null, newsnapshot.getName(), true); - } + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + List templates = _mockVolumeDao.findByStorageIdAndType(storage.getId(), + MockVolumeType.TEMPLATE); - @Override - public Answer DeleteSnapshotBackup(DeleteSnapshotBackupCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - MockVolumeVO backSnapshot = _mockVolumeDao.findByName(cmd.getSnapshotUuid()); - if (backSnapshot == null) { - return new Answer(cmd, false, "can't find the backupsnapshot: " + cmd.getSnapshotUuid()); - } - _mockVolumeDao.remove(backSnapshot.getId()); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when deleting snapshot"); - } finally { - txn.close(); + Map templateInfos = new HashMap(); + for (MockVolumeVO template : templates) { + templateInfos.put(template.getName(), new TemplateInfo(template.getName(), template.getPath() + .replaceAll(storage.getMountPoint(), ""), template.getSize(), template.getSize(), true, false)); + } + txn.commit(); + return new ListTemplateAnswer(cmd.getSecUrl(), templateInfos); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when finding template on sec storage " + storage.getId(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - return new Answer(cmd); - } + } + } - @Override - public CreateVolumeFromSnapshotAnswer CreateVolumeFromSnapshot(CreateVolumeFromSnapshotCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockVolumeVO backSnapshot = null; - MockStoragePoolVO primary = null; - try { - txn.start(); - backSnapshot = _mockVolumeDao.findByName(cmd.getSnapshotUuid()); - if (backSnapshot == null) { - return new CreateVolumeFromSnapshotAnswer(cmd, false, "can't find the backupsnapshot: " - + cmd.getSnapshotUuid(), null); - } + @Override + public Answer Destroy(DestroyCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + MockVolumeVO volume = _mockVolumeDao.findByStoragePathAndType(cmd.getVolume().getPath()); + if (volume != null) { + _mockVolumeDao.remove(volume.getId()); + } - primary = _mockStoragePoolDao.findByUuid(cmd.getPrimaryStoragePoolNameLabel()); - if (primary == null) { - return new CreateVolumeFromSnapshotAnswer(cmd, false, "can't find the primary storage: " - + cmd.getPrimaryStoragePoolNameLabel(), null); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when creating volume from snapshot", ex); - } finally { - txn.close(); + if (cmd.getVmName() != null) { + MockVm vm = _mockVMDao.findByVmName(cmd.getVmName()); + vm.setState(State.Expunging); + if (vm != null) { + MockVMVO vmVo = _mockVMDao.createForUpdate(vm.getId()); + _mockVMDao.update(vm.getId(), vmVo); + } + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when destroying volume " + cmd.getVolume().getPath(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } + return new Answer(cmd); + } - String uuid = UUID.randomUUID().toString(); - MockVolumeVO volume = new MockVolumeVO(); - - volume.setName(uuid); - volume.setPath(primary.getMountPoint() + uuid); - volume.setPoolId(primary.getId()); - volume.setSize(backSnapshot.getSize()); - volume.setStatus(Status.DOWNLOADED); - volume.setType(MockVolumeType.VOLUME); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - _mockVolumeDao.persist(volume); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when creating volume from snapshot " + volume, ex); - } finally { - txn.close(); + @Override + public DownloadAnswer Download(DownloadCommand cmd) { + MockSecStorageVO ssvo = null; + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + ssvo = _mockSecStorageDao.findByUrl(cmd.getSecUrl()); + if (ssvo == null) { + return new DownloadAnswer("can't find secondary storage", + VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error accessing secondary storage " + cmd.getSecUrl(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - return new CreateVolumeFromSnapshotAnswer(cmd, true, null, volume.getPath()); - } - - @Override - public Answer DeleteTemplate(DeleteTemplateCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - MockVolumeVO template = _mockVolumeDao.findByStoragePathAndType(cmd.getTemplatePath()); - if (template == null) { - return new Answer(cmd, false, "can't find template:" + cmd.getTemplatePath()); - } - _mockVolumeDao.remove(template.getId()); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when deleting template"); - } finally { - txn.close(); + MockVolumeVO volume = new MockVolumeVO(); + volume.setPoolId(ssvo.getId()); + volume.setName(cmd.getName()); + volume.setPath(ssvo.getMountPoint() + cmd.getName()); + volume.setSize(0); + volume.setType(MockVolumeType.TEMPLATE); + volume.setStatus(Status.DOWNLOAD_IN_PROGRESS); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + volume = _mockVolumeDao.persist(volume); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when saving volume " + volume, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - return new Answer(cmd); - } + } + return new DownloadAnswer(String.valueOf(volume.getId()), 0, "Downloading", Status.DOWNLOAD_IN_PROGRESS, + cmd.getName(), cmd.getName(), volume.getSize(), volume.getSize(), null); + } - @Override - public Answer SecStorageVMSetup(SecStorageVMSetupCommand cmd) { - return new Answer(cmd); - } + @Override + public DownloadAnswer DownloadProcess(DownloadProgressCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + String volumeId = cmd.getJobId(); + MockVolumeVO volume = _mockVolumeDao.findById(Long.parseLong(volumeId)); + if (volume == null) { + return new DownloadAnswer("Can't find the downloading volume", Status.ABANDONED); + } - @Override - public boolean configure(String name, Map params) throws ConfigurationException { - // TODO Auto-generated method stub - return true; - } + long size = Math.min(volume.getSize() + DEFAULT_TEMPLATE_SIZE / 5, DEFAULT_TEMPLATE_SIZE); + volume.setSize(size); - @Override - public boolean start() { - // TODO Auto-generated method stub - return true; - } - - @Override - public boolean stop() { - // TODO Auto-generated method stub - return true; - } - - @Override - public String getName() { - return this.getClass().getSimpleName(); - } - - @Override - public void preinstallTemplates(String url, long zoneId) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockSecStorageVO storage = null; - try { - txn.start(); - storage = _mockSecStorageDao.findByUrl(url); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Unable to find sec storage at " + url, ex); - } finally { - txn.close(); + double volumeSize = volume.getSize(); + double pct = volumeSize / DEFAULT_TEMPLATE_SIZE; + if (pct >= 1.0) { + volume.setStatus(Status.DOWNLOADED); + _mockVolumeDao.update(volume.getId(), volume); + txn.commit(); + return new DownloadAnswer(cmd.getJobId(), 100, cmd, + com.cloud.storage.VMTemplateStorageResourceAssoc.Status.DOWNLOADED, volume.getPath(), + volume.getName()); + } else { + _mockVolumeDao.update(volume.getId(), volume); + txn.commit(); + return new DownloadAnswer(cmd.getJobId(), (int) (pct * 100.0), cmd, + com.cloud.storage.VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS, volume.getPath(), + volume.getName()); + } + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error during download job " + cmd.getJobId(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - if (storage == null) { - storage = new MockSecStorageVO(); - URI uri; - try { - uri = new URI(url); - } catch (URISyntaxException e) { - return; - } + } + } - String nfsHost = uri.getHost(); - String nfsPath = uri.getPath(); - String path = nfsHost + ":" + nfsPath; - String dir = "/mnt/" + UUID.nameUUIDFromBytes(path.getBytes()).toString() + File.separator; + @Override + public GetStorageStatsAnswer GetStorageStats(GetStorageStatsCommand cmd) { + String uuid = cmd.getStorageId(); + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + if (uuid == null) { + String secUrl = cmd.getSecUrl(); + MockSecStorageVO secondary = _mockSecStorageDao.findByUrl(secUrl); + if (secondary == null) { + return new GetStorageStatsAnswer(cmd, "Can't find the secondary storage:" + secUrl); + } + Long totalUsed = _mockVolumeDao.findTotalStorageId(secondary.getId()); + txn.commit(); + return new GetStorageStatsAnswer(cmd, secondary.getCapacity(), totalUsed); + } else { + MockStoragePoolVO pool = _mockStoragePoolDao.findByUuid(uuid); + if (pool == null) { + return new GetStorageStatsAnswer(cmd, "Can't find the pool"); + } + Long totalUsed = _mockVolumeDao.findTotalStorageId(pool.getId()); + if (totalUsed == null) { + totalUsed = 0L; + } + txn.commit(); + return new GetStorageStatsAnswer(cmd, pool.getCapacity(), totalUsed); + } + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("DBException during storage stats collection for pool " + uuid, ex); + } finally { + txn.close(); + txn = Transaction.open(Transaction.CLOUD_DB); + txn.close(); + } + } - storage.setUrl(url); - storage.setCapacity(DEFAULT_HOST_STORAGE_SIZE); + @Override + public ManageSnapshotAnswer ManageSnapshot(ManageSnapshotCommand cmd) { + String volPath = cmd.getVolumePath(); + MockVolumeVO volume = null; + MockStoragePoolVO storagePool = null; + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + volume = _mockVolumeDao.findByStoragePathAndType(volPath); + if (volume == null) { + return new ManageSnapshotAnswer(cmd, false, "Can't find the volume"); + } + storagePool = _mockStoragePoolDao.findById(volume.getPoolId()); + if (storagePool == null) { + return new ManageSnapshotAnswer(cmd, false, "Can't find the storage pooll"); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Unable to perform snapshot", ex); + } finally { + txn.close(); + txn = Transaction.open(Transaction.CLOUD_DB); + txn.close(); + } - storage.setMountPoint(dir); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - storage = _mockSecStorageDao.persist(storage); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when saving storage " + storage, ex); - } finally { - txn.close(); + String mountPoint = storagePool.getMountPoint(); + MockVolumeVO snapshot = new MockVolumeVO(); + + snapshot.setName(cmd.getSnapshotName()); + snapshot.setPath(mountPoint + cmd.getSnapshotName()); + snapshot.setSize(volume.getSize()); + snapshot.setPoolId(storagePool.getId()); + snapshot.setType(MockVolumeType.SNAPSHOT); + snapshot.setStatus(Status.DOWNLOADED); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + snapshot = _mockVolumeDao.persist(snapshot); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when saving snapshot " + snapshot, ex); + } finally { + txn.close(); + txn = Transaction.open(Transaction.CLOUD_DB); + txn.close(); + } + + return new ManageSnapshotAnswer(cmd, snapshot.getId(), snapshot.getPath(), true, ""); + } + + @Override + public BackupSnapshotAnswer BackupSnapshot(BackupSnapshotCommand cmd, SimulatorInfo info) { + // emulate xenserver backupsnapshot, if the base volume is deleted, then + // backupsnapshot failed + MockVolumeVO volume = null; + MockVolumeVO snapshot = null; + MockSecStorageVO secStorage = null; + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + volume = _mockVolumeDao.findByStoragePathAndType(cmd.getVolumePath()); + if (volume == null) { + return new BackupSnapshotAnswer(cmd, false, "Can't find base volume: " + cmd.getVolumePath(), null, + true); + } + String snapshotPath = cmd.getSnapshotUuid(); + snapshot = _mockVolumeDao.findByStoragePathAndType(snapshotPath); + if (snapshot == null) { + return new BackupSnapshotAnswer(cmd, false, "can't find snapshot" + snapshotPath, null, true); + } + + String secStorageUrl = cmd.getSecondaryStorageUrl(); + secStorage = _mockSecStorageDao.findByUrl(secStorageUrl); + if (secStorage == null) { + return new BackupSnapshotAnswer(cmd, false, "can't find sec storage" + snapshotPath, null, true); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when backing up snapshot"); + } finally { + txn.close(); + txn = Transaction.open(Transaction.CLOUD_DB); + txn.close(); + } + + MockVolumeVO newsnapshot = new MockVolumeVO(); + String name = UUID.randomUUID().toString(); + newsnapshot.setName(name); + newsnapshot.setPath(secStorage.getMountPoint() + name); + newsnapshot.setPoolId(secStorage.getId()); + newsnapshot.setSize(snapshot.getSize()); + newsnapshot.setStatus(Status.DOWNLOADED); + newsnapshot.setType(MockVolumeType.SNAPSHOT); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + snapshot = _mockVolumeDao.persist(snapshot); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when backing up snapshot " + newsnapshot, ex); + } finally { + txn.close(); + txn = Transaction.open(Transaction.CLOUD_DB); + txn.close(); + } + + return new BackupSnapshotAnswer(cmd, true, null, newsnapshot.getName(), true); + } + + @Override + public Answer DeleteSnapshotBackup(DeleteSnapshotBackupCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + MockVolumeVO backSnapshot = _mockVolumeDao.findByName(cmd.getSnapshotUuid()); + if (backSnapshot == null) { + return new Answer(cmd, false, "can't find the backupsnapshot: " + cmd.getSnapshotUuid()); + } + _mockVolumeDao.remove(backSnapshot.getId()); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when deleting snapshot"); + } finally { + txn.close(); + txn = Transaction.open(Transaction.CLOUD_DB); + txn.close(); + } + return new Answer(cmd); + } + + @Override + public CreateVolumeFromSnapshotAnswer CreateVolumeFromSnapshot(CreateVolumeFromSnapshotCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockVolumeVO backSnapshot = null; + MockStoragePoolVO primary = null; + try { + txn.start(); + backSnapshot = _mockVolumeDao.findByName(cmd.getSnapshotUuid()); + if (backSnapshot == null) { + return new CreateVolumeFromSnapshotAnswer(cmd, false, "can't find the backupsnapshot: " + + cmd.getSnapshotUuid(), null); + } + + primary = _mockStoragePoolDao.findByUuid(cmd.getPrimaryStoragePoolNameLabel()); + if (primary == null) { + return new CreateVolumeFromSnapshotAnswer(cmd, false, "can't find the primary storage: " + + cmd.getPrimaryStoragePoolNameLabel(), null); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when creating volume from snapshot", ex); + } finally { + txn.close(); + txn = Transaction.open(Transaction.CLOUD_DB); + txn.close(); + } + + String uuid = UUID.randomUUID().toString(); + MockVolumeVO volume = new MockVolumeVO(); + + volume.setName(uuid); + volume.setPath(primary.getMountPoint() + uuid); + volume.setPoolId(primary.getId()); + volume.setSize(backSnapshot.getSize()); + volume.setStatus(Status.DOWNLOADED); + volume.setType(MockVolumeType.VOLUME); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + _mockVolumeDao.persist(volume); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when creating volume from snapshot " + volume, ex); + } finally { + txn.close(); + txn = Transaction.open(Transaction.CLOUD_DB); + txn.close(); + } + + return new CreateVolumeFromSnapshotAnswer(cmd, true, null, volume.getPath()); + } + + @Override + public Answer DeleteTemplate(DeleteTemplateCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + MockVolumeVO template = _mockVolumeDao.findByStoragePathAndType(cmd.getTemplatePath()); + if (template == null) { + return new Answer(cmd, false, "can't find template:" + cmd.getTemplatePath()); + } + _mockVolumeDao.remove(template.getId()); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when deleting template"); + } finally { + txn.close(); + txn = Transaction.open(Transaction.CLOUD_DB); + txn.close(); + } + return new Answer(cmd); + } + + @Override + public Answer SecStorageVMSetup(SecStorageVMSetupCommand cmd) { + return new Answer(cmd); + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + // TODO Auto-generated method stub + return true; + } + + @Override + public boolean start() { + // TODO Auto-generated method stub + return true; + } + + @Override + public boolean stop() { + // TODO Auto-generated method stub + return true; + } + + @Override + public String getName() { + return this.getClass().getSimpleName(); + } + + @Override + public void preinstallTemplates(String url, long zoneId) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockSecStorageVO storage = null; + try { + txn.start(); + storage = _mockSecStorageDao.findByUrl(url); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Unable to find sec storage at " + url, ex); + } finally { + txn.close(); + txn = Transaction.open(Transaction.CLOUD_DB); + txn.close(); + } + if (storage == null) { + storage = new MockSecStorageVO(); + URI uri; + try { + uri = new URI(url); + } catch (URISyntaxException e) { + return; + } + + String nfsHost = uri.getHost(); + String nfsPath = uri.getPath(); + String path = nfsHost + ":" + nfsPath; + String dir = "/mnt/" + UUID.nameUUIDFromBytes(path.getBytes()).toString() + File.separator; + + storage.setUrl(url); + storage.setCapacity(DEFAULT_HOST_STORAGE_SIZE); + + storage.setMountPoint(dir); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + storage = _mockSecStorageDao.persist(storage); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when saving storage " + storage, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - // preinstall default templates into secondary storage - long defaultTemplateSize = 2 * 1024 * 1024 * 1024L; - MockVolumeVO template = new MockVolumeVO(); - template.setName("simulator-domR"); - template.setPath(storage.getMountPoint() + "template/tmpl/1/10/" + UUID.randomUUID().toString()); - template.setPoolId(storage.getId()); - template.setSize(defaultTemplateSize); - template.setType(MockVolumeType.TEMPLATE); - template.setStatus(Status.DOWNLOADED); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - template = _mockVolumeDao.persist(template); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when saving template " + template, ex); - } finally { - txn.close(); + // preinstall default templates into secondary storage + long defaultTemplateSize = 2 * 1024 * 1024 * 1024L; + MockVolumeVO template = new MockVolumeVO(); + template.setName("simulator-domR"); + template.setPath(storage.getMountPoint() + "template/tmpl/1/10/" + UUID.randomUUID().toString()); + template.setPoolId(storage.getId()); + template.setSize(defaultTemplateSize); + template.setType(MockVolumeType.TEMPLATE); + template.setStatus(Status.DOWNLOADED); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + template = _mockVolumeDao.persist(template); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when saving template " + template, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - template = new MockVolumeVO(); - template.setName("simulator-Centos"); - template.setPath(storage.getMountPoint() + "template/tmpl/1/11/" + UUID.randomUUID().toString()); - template.setPoolId(storage.getId()); - template.setSize(defaultTemplateSize); - template.setType(MockVolumeType.TEMPLATE); - template.setStatus(Status.DOWNLOADED); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - template = _mockVolumeDao.persist(template); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when saving template " + template, ex); - } finally { - txn.close(); + template = new MockVolumeVO(); + template.setName("simulator-Centos"); + template.setPath(storage.getMountPoint() + "template/tmpl/1/11/" + UUID.randomUUID().toString()); + template.setPoolId(storage.getId()); + template.setSize(defaultTemplateSize); + template.setType(MockVolumeType.TEMPLATE); + template.setStatus(Status.DOWNLOADED); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + template = _mockVolumeDao.persist(template); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when saving template " + template, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } + } + } - } + } - @Override - public StoragePoolInfo getLocalStorage(String hostGuid) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockHost host = null; - MockStoragePoolVO storagePool = null; - try { - txn.start(); - host = _mockHostDao.findByGuid(hostGuid); - storagePool = _mockStoragePoolDao.findByHost(hostGuid); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Unable to find host " + hostGuid, ex); - } finally { - txn.close(); + @Override + public StoragePoolInfo getLocalStorage(String hostGuid) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockHost host = null; + MockStoragePoolVO storagePool = null; + try { + txn.start(); + host = _mockHostDao.findByGuid(hostGuid); + storagePool = _mockStoragePoolDao.findByHost(hostGuid); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Unable to find host " + hostGuid, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - if (storagePool == null) { - String uuid = UUID.randomUUID().toString(); - storagePool = new MockStoragePoolVO(); - storagePool.setUuid(uuid); - storagePool.setMountPoint("/mnt/" + uuid + File.separator); - storagePool.setCapacity(DEFAULT_HOST_STORAGE_SIZE); - storagePool.setHostGuid(hostGuid); - storagePool.setStorageType(StoragePoolType.Filesystem); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - storagePool = _mockStoragePoolDao.persist(storagePool); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when saving storagePool " + storagePool, ex); - } finally { - txn.close(); + if (storagePool == null) { + String uuid = UUID.randomUUID().toString(); + storagePool = new MockStoragePoolVO(); + storagePool.setUuid(uuid); + storagePool.setMountPoint("/mnt/" + uuid + File.separator); + storagePool.setCapacity(DEFAULT_HOST_STORAGE_SIZE); + storagePool.setHostGuid(hostGuid); + storagePool.setStorageType(StoragePoolType.Filesystem); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + storagePool = _mockStoragePoolDao.persist(storagePool); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when saving storagePool " + storagePool, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } - return new StoragePoolInfo(storagePool.getUuid(), host.getPrivateIpAddress(), storagePool.getMountPoint(), - storagePool.getMountPoint(), storagePool.getPoolType(), storagePool.getCapacity(), 0); - } + } + } + return new StoragePoolInfo(storagePool.getUuid(), host.getPrivateIpAddress(), storagePool.getMountPoint(), + storagePool.getMountPoint(), storagePool.getPoolType(), storagePool.getCapacity(), 0); + } - @Override - public StoragePoolInfo getLocalStorage(String hostGuid, Long storageSize) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockHost host = null; - try { - txn.start(); - host = _mockHostDao.findByGuid(hostGuid); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Unable to find host " + hostGuid, ex); - } finally { - txn.close(); + @Override + public StoragePoolInfo getLocalStorage(String hostGuid, Long storageSize) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockHost host = null; + try { + txn.start(); + host = _mockHostDao.findByGuid(hostGuid); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Unable to find host " + hostGuid, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - if (storageSize == null) { - storageSize = DEFAULT_HOST_STORAGE_SIZE; - } - txn = Transaction.open(Transaction.SIMULATOR_DB); - MockStoragePoolVO storagePool = null; - try { - txn.start(); - storagePool = _mockStoragePoolDao.findByHost(hostGuid); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when finding storagePool " + storagePool, ex); - } finally { - txn.close(); + } + if (storageSize == null) { + storageSize = DEFAULT_HOST_STORAGE_SIZE; + } + txn = Transaction.open(Transaction.SIMULATOR_DB); + MockStoragePoolVO storagePool = null; + try { + txn.start(); + storagePool = _mockStoragePoolDao.findByHost(hostGuid); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when finding storagePool " + storagePool, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - if (storagePool == null) { - String uuid = UUID.randomUUID().toString(); - storagePool = new MockStoragePoolVO(); - storagePool.setUuid(uuid); - storagePool.setMountPoint("/mnt/" + uuid + File.separator); - storagePool.setCapacity(storageSize); - storagePool.setHostGuid(hostGuid); - storagePool.setStorageType(StoragePoolType.Filesystem); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - storagePool = _mockStoragePoolDao.persist(storagePool); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when saving storagePool " + storagePool, ex); - } finally { - txn.close(); + } + if (storagePool == null) { + String uuid = UUID.randomUUID().toString(); + storagePool = new MockStoragePoolVO(); + storagePool.setUuid(uuid); + storagePool.setMountPoint("/mnt/" + uuid + File.separator); + storagePool.setCapacity(storageSize); + storagePool.setHostGuid(hostGuid); + storagePool.setStorageType(StoragePoolType.Filesystem); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + storagePool = _mockStoragePoolDao.persist(storagePool); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when saving storagePool " + storagePool, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } - return new StoragePoolInfo(storagePool.getUuid(), host.getPrivateIpAddress(), storagePool.getMountPoint(), - storagePool.getMountPoint(), storagePool.getPoolType(), storagePool.getCapacity(), 0); - } + } + } + return new StoragePoolInfo(storagePool.getUuid(), host.getPrivateIpAddress(), storagePool.getMountPoint(), + storagePool.getMountPoint(), storagePool.getPoolType(), storagePool.getCapacity(), 0); + } - @Override - public CreatePrivateTemplateAnswer CreatePrivateTemplateFromSnapshot(CreatePrivateTemplateFromSnapshotCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockVolumeVO snapshot = null; - MockSecStorageVO sec = null; - try { - txn.start(); - String snapshotUUId = cmd.getSnapshotUuid(); - snapshot = _mockVolumeDao.findByName(snapshotUUId); - if (snapshot == null) { - snapshotUUId = cmd.getSnapshotName(); - snapshot = _mockVolumeDao.findByName(snapshotUUId); - if (snapshot == null) { - return new CreatePrivateTemplateAnswer(cmd, false, "can't find snapshot:" + snapshotUUId); - } - } + @Override + public CreatePrivateTemplateAnswer CreatePrivateTemplateFromSnapshot(CreatePrivateTemplateFromSnapshotCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockVolumeVO snapshot = null; + MockSecStorageVO sec = null; + try { + txn.start(); + String snapshotUUId = cmd.getSnapshotUuid(); + snapshot = _mockVolumeDao.findByName(snapshotUUId); + if (snapshot == null) { + snapshotUUId = cmd.getSnapshotName(); + snapshot = _mockVolumeDao.findByName(snapshotUUId); + if (snapshot == null) { + return new CreatePrivateTemplateAnswer(cmd, false, "can't find snapshot:" + snapshotUUId); + } + } - sec = _mockSecStorageDao.findByUrl(cmd.getSecondaryStorageUrl()); - if (sec == null) { - return new CreatePrivateTemplateAnswer(cmd, false, "can't find secondary storage"); - } - txn.commit(); - } finally { - txn.close(); + sec = _mockSecStorageDao.findByUrl(cmd.getSecondaryStorageUrl()); + if (sec == null) { + return new CreatePrivateTemplateAnswer(cmd, false, "can't find secondary storage"); + } + txn.commit(); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - MockVolumeVO template = new MockVolumeVO(); - String uuid = UUID.randomUUID().toString(); - template.setName(uuid); - template.setPath(sec.getMountPoint() + uuid); - template.setPoolId(sec.getId()); - template.setSize(snapshot.getSize()); - template.setStatus(Status.DOWNLOADED); - template.setType(MockVolumeType.TEMPLATE); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - template = _mockVolumeDao.persist(template); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when saving template " + template, ex); - } finally { - txn.close(); + MockVolumeVO template = new MockVolumeVO(); + String uuid = UUID.randomUUID().toString(); + template.setName(uuid); + template.setPath(sec.getMountPoint() + uuid); + template.setPoolId(sec.getId()); + template.setSize(snapshot.getSize()); + template.setStatus(Status.DOWNLOADED); + template.setType(MockVolumeType.TEMPLATE); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + template = _mockVolumeDao.persist(template); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when saving template " + template, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - return new CreatePrivateTemplateAnswer(cmd, true, "", template.getName(), template.getSize(), - template.getSize(), template.getName(), ImageFormat.QCOW2); - } + return new CreatePrivateTemplateAnswer(cmd, true, "", template.getName(), template.getSize(), + template.getSize(), template.getName(), ImageFormat.QCOW2); + } - @Override - public Answer ComputeChecksum(ComputeChecksumCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - MockVolumeVO volume = _mockVolumeDao.findByName(cmd.getTemplatePath()); - if (volume == null) { - return new Answer(cmd, false, "cant' find volume:" + cmd.getTemplatePath()); - } - String md5 = null; - try { - MessageDigest md = MessageDigest.getInstance("md5"); - md5 = String.format("%032x", new BigInteger(1, md.digest(cmd.getTemplatePath().getBytes()))); - } catch (NoSuchAlgorithmException e) { - s_logger.debug("failed to gernerate md5:" + e.toString()); - } - txn.commit(); - return new Answer(cmd, true, md5); - } finally { - txn.close(); + @Override + public Answer ComputeChecksum(ComputeChecksumCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + MockVolumeVO volume = _mockVolumeDao.findByName(cmd.getTemplatePath()); + if (volume == null) { + return new Answer(cmd, false, "cant' find volume:" + cmd.getTemplatePath()); + } + String md5 = null; + try { + MessageDigest md = MessageDigest.getInstance("md5"); + md5 = String.format("%032x", new BigInteger(1, md.digest(cmd.getTemplatePath().getBytes()))); + } catch (NoSuchAlgorithmException e) { + s_logger.debug("failed to gernerate md5:" + e.toString()); + } + txn.commit(); + return new Answer(cmd, true, md5); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } + } + } - @Override - public CreatePrivateTemplateAnswer CreatePrivateTemplateFromVolume(CreatePrivateTemplateFromVolumeCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockVolumeVO volume = null; - MockSecStorageVO sec = null; - try { - txn.start(); - volume = _mockVolumeDao.findByStoragePathAndType(cmd.getVolumePath()); - if (volume == null) { - return new CreatePrivateTemplateAnswer(cmd, false, "cant' find volume" + cmd.getVolumePath()); - } + @Override + public CreatePrivateTemplateAnswer CreatePrivateTemplateFromVolume(CreatePrivateTemplateFromVolumeCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockVolumeVO volume = null; + MockSecStorageVO sec = null; + try { + txn.start(); + volume = _mockVolumeDao.findByStoragePathAndType(cmd.getVolumePath()); + if (volume == null) { + return new CreatePrivateTemplateAnswer(cmd, false, "cant' find volume" + cmd.getVolumePath()); + } - sec = _mockSecStorageDao.findByUrl(cmd.getSecondaryStorageUrl()); - if (sec == null) { - return new CreatePrivateTemplateAnswer(cmd, false, "can't find secondary storage"); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Error when creating private template from volume"); - } finally { - txn.close(); + sec = _mockSecStorageDao.findByUrl(cmd.getSecondaryStorageUrl()); + if (sec == null) { + return new CreatePrivateTemplateAnswer(cmd, false, "can't find secondary storage"); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Error when creating private template from volume"); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - MockVolumeVO template = new MockVolumeVO(); - String uuid = UUID.randomUUID().toString(); - template.setName(uuid); - template.setPath(sec.getMountPoint() + uuid); - template.setPoolId(sec.getId()); - template.setSize(volume.getSize()); - template.setStatus(Status.DOWNLOADED); - template.setType(MockVolumeType.TEMPLATE); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - template = _mockVolumeDao.persist(template); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Encountered " + ex.getMessage() + " when persisting template " - + template.getName(), ex); - } finally { - txn.close(); + MockVolumeVO template = new MockVolumeVO(); + String uuid = UUID.randomUUID().toString(); + template.setName(uuid); + template.setPath(sec.getMountPoint() + uuid); + template.setPoolId(sec.getId()); + template.setSize(volume.getSize()); + template.setStatus(Status.DOWNLOADED); + template.setType(MockVolumeType.TEMPLATE); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + template = _mockVolumeDao.persist(template); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Encountered " + ex.getMessage() + " when persisting template " + + template.getName(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - return new CreatePrivateTemplateAnswer(cmd, true, "", template.getName(), template.getSize(), - template.getSize(), template.getName(), ImageFormat.QCOW2); - } + return new CreatePrivateTemplateAnswer(cmd, true, "", template.getName(), template.getSize(), + template.getSize(), template.getName(), ImageFormat.QCOW2); + } - @Override - public CopyVolumeAnswer CopyVolume(CopyVolumeCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - boolean toSecondaryStorage = cmd.toSecondaryStorage(); - MockSecStorageVO sec = null; - MockStoragePoolVO primaryStorage = null; - try { - txn.start(); - sec = _mockSecStorageDao.findByUrl(cmd.getSecondaryStorageURL()); - if (sec == null) { - return new CopyVolumeAnswer(cmd, false, "can't find secondary storage", null, null); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Encountered " + ex.getMessage() + " when accessing secondary at " - + cmd.getSecondaryStorageURL(), ex); - } finally { - txn.close(); + @Override + public CopyVolumeAnswer CopyVolume(CopyVolumeCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + boolean toSecondaryStorage = cmd.toSecondaryStorage(); + MockSecStorageVO sec = null; + MockStoragePoolVO primaryStorage = null; + try { + txn.start(); + sec = _mockSecStorageDao.findByUrl(cmd.getSecondaryStorageURL()); + if (sec == null) { + return new CopyVolumeAnswer(cmd, false, "can't find secondary storage", null, null); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Encountered " + ex.getMessage() + " when accessing secondary at " + + cmd.getSecondaryStorageURL(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - primaryStorage = _mockStoragePoolDao.findByUuid(cmd.getPool().getUuid()); - if (primaryStorage == null) { - return new CopyVolumeAnswer(cmd, false, "Can't find primary storage", null, null); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Encountered " + ex.getMessage() + " when accessing primary at " - + cmd.getPool(), ex); - } finally { - txn.close(); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + primaryStorage = _mockStoragePoolDao.findByUuid(cmd.getPool().getUuid()); + if (primaryStorage == null) { + return new CopyVolumeAnswer(cmd, false, "Can't find primary storage", null, null); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Encountered " + ex.getMessage() + " when accessing primary at " + + cmd.getPool(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - MockVolumeVO volume = null; - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - volume = _mockVolumeDao.findByStoragePathAndType(cmd.getVolumePath()); - if (volume == null) { - return new CopyVolumeAnswer(cmd, false, "cant' find volume" + cmd.getVolumePath(), null, null); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Encountered " + ex.getMessage() + " when accessing volume at " - + cmd.getVolumePath(), ex); - } finally { - txn.close(); + MockVolumeVO volume = null; + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + volume = _mockVolumeDao.findByStoragePathAndType(cmd.getVolumePath()); + if (volume == null) { + return new CopyVolumeAnswer(cmd, false, "cant' find volume" + cmd.getVolumePath(), null, null); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Encountered " + ex.getMessage() + " when accessing volume at " + + cmd.getVolumePath(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } - String name = UUID.randomUUID().toString(); - if (toSecondaryStorage) { - MockVolumeVO vol = new MockVolumeVO(); - vol.setName(name); - vol.setPath(sec.getMountPoint() + name); - vol.setPoolId(sec.getId()); - vol.setSize(volume.getSize()); - vol.setStatus(Status.DOWNLOADED); - vol.setType(MockVolumeType.VOLUME); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - vol = _mockVolumeDao.persist(vol); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Encountered " + ex.getMessage() + " when persisting volume " - + vol.getName(), ex); - } finally { - txn.close(); + String name = UUID.randomUUID().toString(); + if (toSecondaryStorage) { + MockVolumeVO vol = new MockVolumeVO(); + vol.setName(name); + vol.setPath(sec.getMountPoint() + name); + vol.setPoolId(sec.getId()); + vol.setSize(volume.getSize()); + vol.setStatus(Status.DOWNLOADED); + vol.setType(MockVolumeType.VOLUME); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + vol = _mockVolumeDao.persist(vol); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Encountered " + ex.getMessage() + " when persisting volume " + + vol.getName(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - return new CopyVolumeAnswer(cmd, true, null, sec.getMountPoint(), vol.getPath()); - } else { - MockVolumeVO vol = new MockVolumeVO(); - vol.setName(name); - vol.setPath(primaryStorage.getMountPoint() + name); - vol.setPoolId(primaryStorage.getId()); - vol.setSize(volume.getSize()); - vol.setStatus(Status.DOWNLOADED); - vol.setType(MockVolumeType.VOLUME); - txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - vol = _mockVolumeDao.persist(vol); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Encountered " + ex.getMessage() + " when persisting volume " - + vol.getName(), ex); - } finally { - txn.close(); + } + return new CopyVolumeAnswer(cmd, true, null, sec.getMountPoint(), vol.getPath()); + } else { + MockVolumeVO vol = new MockVolumeVO(); + vol.setName(name); + vol.setPath(primaryStorage.getMountPoint() + name); + vol.setPoolId(primaryStorage.getId()); + vol.setSize(volume.getSize()); + vol.setStatus(Status.DOWNLOADED); + vol.setType(MockVolumeType.VOLUME); + txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + vol = _mockVolumeDao.persist(vol); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Encountered " + ex.getMessage() + " when persisting volume " + + vol.getName(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - return new CopyVolumeAnswer(cmd, true, null, primaryStorage.getMountPoint(), vol.getPath()); - } - } + } + return new CopyVolumeAnswer(cmd, true, null, primaryStorage.getMountPoint(), vol.getPath()); + } + } } diff --git a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java index 69c066d4577..63c04be0c81 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java +++ b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/MockVmManagerImpl.java @@ -23,20 +23,56 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.ejb.Local; +import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.agent.api.*; -import com.cloud.agent.api.routing.*; -import com.cloud.network.router.VirtualRouter; import org.apache.log4j.Logger; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.BumpUpPriorityCommand; +import com.cloud.agent.api.CheckRouterAnswer; +import com.cloud.agent.api.CheckRouterCommand; +import com.cloud.agent.api.CheckVirtualMachineAnswer; +import com.cloud.agent.api.CheckVirtualMachineCommand; +import com.cloud.agent.api.CleanupNetworkRulesCmd; +import com.cloud.agent.api.GetDomRVersionAnswer; +import com.cloud.agent.api.GetDomRVersionCmd; +import com.cloud.agent.api.GetVmStatsAnswer; +import com.cloud.agent.api.GetVmStatsCommand; +import com.cloud.agent.api.GetVncPortAnswer; +import com.cloud.agent.api.GetVncPortCommand; +import com.cloud.agent.api.MigrateAnswer; +import com.cloud.agent.api.MigrateCommand; +import com.cloud.agent.api.NetworkUsageAnswer; +import com.cloud.agent.api.NetworkUsageCommand; +import com.cloud.agent.api.PrepareForMigrationAnswer; +import com.cloud.agent.api.PrepareForMigrationCommand; +import com.cloud.agent.api.RebootAnswer; +import com.cloud.agent.api.RebootCommand; +import com.cloud.agent.api.SecurityGroupRuleAnswer; +import com.cloud.agent.api.SecurityGroupRulesCmd; +import com.cloud.agent.api.StartAnswer; +import com.cloud.agent.api.StartCommand; +import com.cloud.agent.api.StopAnswer; +import com.cloud.agent.api.StopCommand; +import com.cloud.agent.api.VmStatsEntry; import com.cloud.agent.api.check.CheckSshAnswer; import com.cloud.agent.api.check.CheckSshCommand; import com.cloud.agent.api.proxy.CheckConsoleProxyLoadCommand; import com.cloud.agent.api.proxy.WatchConsoleProxyLoadCommand; +import com.cloud.agent.api.routing.DhcpEntryCommand; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.LoadBalancerConfigCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.SavePasswordCommand; +import com.cloud.agent.api.routing.SetFirewallRulesCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; +import com.cloud.agent.api.routing.VmDataCommand; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.network.Networks.TrafficType; +import com.cloud.network.router.VirtualRouter; import com.cloud.simulator.MockHost; import com.cloud.simulator.MockSecurityRulesVO; import com.cloud.simulator.MockVMVO; @@ -46,7 +82,6 @@ import com.cloud.simulator.dao.MockSecurityRulesDao; import com.cloud.simulator.dao.MockVMDao; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; - import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine.State; @@ -55,46 +90,46 @@ import com.cloud.vm.VirtualMachine.State; public class MockVmManagerImpl implements MockVmManager { private static final Logger s_logger = Logger.getLogger(MockVmManagerImpl.class); - @Inject MockVMDao _mockVmDao = null; - @Inject MockAgentManager _mockAgentMgr = null; - @Inject MockHostDao _mockHostDao = null; - @Inject MockSecurityRulesDao _mockSecurityDao = null; - private Map>> _securityRules = new ConcurrentHashMap>>(); + @Inject MockVMDao _mockVmDao = null; + @Inject MockAgentManager _mockAgentMgr = null; + @Inject MockHostDao _mockHostDao = null; + @Inject MockSecurityRulesDao _mockSecurityDao = null; + private final Map>> _securityRules = new ConcurrentHashMap>>(); - public MockVmManagerImpl() { - } + public MockVmManagerImpl() { + } - @Override + @Override public boolean configure(String name, Map params) throws ConfigurationException { - return true; - } + return true; + } public String startVM(String vmName, NicTO[] nics, - int cpuHz, long ramSize, - String bootArgs, String hostGuid) { + int cpuHz, long ramSize, + String bootArgs, String hostGuid) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - MockHost host = null; - MockVm vm = null; - try { - txn.start(); - host = _mockHostDao.findByGuid(hostGuid); - if (host == null) { - return "can't find host"; - } + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + MockHost host = null; + MockVm vm = null; + try { + txn.start(); + host = _mockHostDao.findByGuid(hostGuid); + if (host == null) { + return "can't find host"; + } - vm = _mockVmDao.findByVmName(vmName); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Unable to start VM " + vmName, ex); - } finally { - txn.close(); + vm = _mockVmDao.findByVmName(vmName); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Unable to start VM " + vmName, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } if(vm == null) { int vncPort = 0; @@ -109,43 +144,43 @@ public class MockVmManagerImpl implements MockVmManager { vm.setHostId(host.getId()); vm.setBootargs(bootArgs); if(vmName.startsWith("s-")) { - vm.setType("SecondaryStorageVm"); + vm.setType("SecondaryStorageVm"); } else if (vmName.startsWith("v-")) { - vm.setType("ConsoleProxy"); + vm.setType("ConsoleProxy"); } else if (vmName.startsWith("r-")) { - vm.setType("DomainRouter"); + vm.setType("DomainRouter"); } else if (vmName.startsWith("i-")) { - vm.setType("User"); + vm.setType("User"); } txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - vm = _mockVmDao.persist((MockVMVO) vm); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("unable to save vm to db " + vm.getName(), ex); - } finally { - txn.close(); + try { + txn.start(); + vm = _mockVmDao.persist((MockVMVO) vm); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("unable to save vm to db " + vm.getName(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } } else { if(vm.getState() == State.Stopped) { vm.setState(State.Running); txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - _mockVmDao.update(vm.getId(), (MockVMVO)vm); - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("unable to update vm " + vm.getName(), ex); - } finally { - txn.close(); + try { + txn.start(); + _mockVmDao.update(vm.getId(), (MockVMVO)vm); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("unable to update vm " + vm.getName(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } + } } } @@ -192,49 +227,49 @@ public class MockVmManagerImpl implements MockVmManager { return null; } - public boolean rebootVM(String vmName) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - MockVm vm = _mockVmDao.findByVmName(vmName); - if (vm != null) { - vm.setState(State.Running); - _mockVmDao.update(vm.getId(), (MockVMVO) vm); + public boolean rebootVM(String vmName) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + MockVm vm = _mockVmDao.findByVmName(vmName); + if (vm != null) { + vm.setState(State.Running); + _mockVmDao.update(vm.getId(), (MockVMVO) vm); - } - txn.commit(); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("unable to reboot vm " + vmName, ex); - } finally { - txn.close(); + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("unable to reboot vm " + vmName, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - return true; - } + } + return true; + } - @Override - public Map getVms(String hostGuid) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - List vms = _mockVmDao.findByHostGuid(hostGuid); - Map vmMap = new HashMap(); - for (MockVMVO vm : vms) { - vmMap.put(vm.getName(), vm); - } - txn.commit(); - return vmMap; - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("unable to fetch vms from host " + hostGuid, ex); - } finally { - txn.close(); + @Override + public Map getVms(String hostGuid) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + List vms = _mockVmDao.findByHostGuid(hostGuid); + Map vmMap = new HashMap(); + for (MockVMVO vm : vms) { + vmMap.put(vm.getName(), vm); + } + txn.commit(); + return vmMap; + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("unable to fetch vms from host " + hostGuid, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } + } + } @Override public CheckRouterAnswer checkRouter(CheckRouterCommand cmd) { @@ -267,30 +302,30 @@ public class MockVmManagerImpl implements MockVmManager { } @Override - public Map getVmStates(String hostGuid) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - Map states = new HashMap(); - List vms = _mockVmDao.findByHostGuid(hostGuid); - if (vms.isEmpty()) { - txn.commit(); - return states; - } - for (MockVm vm : vms) { - states.put(vm.getName(), vm.getState()); - } - txn.commit(); - return states; - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("unable to fetch vms from host " + hostGuid, ex); - } finally { - txn.close(); + public Map getVmStates(String hostGuid) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + Map states = new HashMap(); + List vms = _mockVmDao.findByHostGuid(hostGuid); + if (vms.isEmpty()) { + txn.commit(); + return states; + } + for (MockVm vm : vms) { + states.put(vm.getName(), vm.getState()); + } + txn.commit(); + return states; + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("unable to fetch vms from host " + hostGuid, ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } + } + } @Override public boolean start() { @@ -323,26 +358,26 @@ public class MockVmManagerImpl implements MockVmManager { } @Override - public CheckVirtualMachineAnswer checkVmState(CheckVirtualMachineCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - MockVMVO vm = _mockVmDao.findByVmName(cmd.getVmName()); - if (vm == null) { - return new CheckVirtualMachineAnswer(cmd, "can't find vm:" + cmd.getVmName()); - } + public CheckVirtualMachineAnswer checkVmState(CheckVirtualMachineCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + MockVMVO vm = _mockVmDao.findByVmName(cmd.getVmName()); + if (vm == null) { + return new CheckVirtualMachineAnswer(cmd, "can't find vm:" + cmd.getVmName()); + } - txn.commit(); - return new CheckVirtualMachineAnswer(cmd, vm.getState(), vm.getVncPort()); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("unable to fetch vm state " + cmd.getVmName(), ex); - } finally { - txn.close(); + txn.commit(); + return new CheckVirtualMachineAnswer(cmd, vm.getState(), vm.getVncPort()); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("unable to fetch vm state " + cmd.getVmName(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } + } + } @Override public Answer startVM(StartCommand cmd, SimulatorInfo info) { @@ -372,7 +407,7 @@ public class MockVmManagerImpl implements MockVmManager { @Override public Answer SetFirewallRules(SetFirewallRulesCommand cmd) { - return new Answer(cmd); + return new Answer(cmd); } @@ -382,38 +417,38 @@ public class MockVmManagerImpl implements MockVmManager { } @Override - public MigrateAnswer Migrate(MigrateCommand cmd, SimulatorInfo info) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - String vmName = cmd.getVmName(); - String destGuid = cmd.getHostGuid(); - MockVMVO vm = _mockVmDao.findByVmNameAndHost(vmName, info.getHostUuid()); - if (vm == null) { - return new MigrateAnswer(cmd, false, "can't find vm:" + vmName + " on host:" + info.getHostUuid(), null); - } else { + public MigrateAnswer Migrate(MigrateCommand cmd, SimulatorInfo info) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + String vmName = cmd.getVmName(); + String destGuid = cmd.getHostGuid(); + MockVMVO vm = _mockVmDao.findByVmNameAndHost(vmName, info.getHostUuid()); + if (vm == null) { + return new MigrateAnswer(cmd, false, "can't find vm:" + vmName + " on host:" + info.getHostUuid(), null); + } else { if (vm.getState() == State.Migrating) { vm.setState(State.Running); } } - MockHost destHost = _mockHostDao.findByGuid(destGuid); - if (destHost == null) { - return new MigrateAnswer(cmd, false, "can;t find host:" + info.getHostUuid(), null); - } - vm.setHostId(destHost.getId()); - _mockVmDao.update(vm.getId(), vm); - txn.commit(); - return new MigrateAnswer(cmd, true, null, 0); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("unable to migrate vm " + cmd.getVmName(), ex); - } finally { - txn.close(); + MockHost destHost = _mockHostDao.findByGuid(destGuid); + if (destHost == null) { + return new MigrateAnswer(cmd, false, "can;t find host:" + info.getHostUuid(), null); + } + vm.setHostId(destHost.getId()); + _mockVmDao.update(vm.getId(), vm); + txn.commit(); + return new MigrateAnswer(cmd, true, null, 0); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("unable to migrate vm " + cmd.getVmName(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } + } + } @Override public PrepareForMigrationAnswer prepareForMigrate(PrepareForMigrationCommand cmd) { @@ -457,81 +492,81 @@ public class MockVmManagerImpl implements MockVmManager { } @Override - public Answer CleanupNetworkRules(CleanupNetworkRulesCmd cmd, SimulatorInfo info) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - List rules = _mockSecurityDao.findByHost(info.getHostUuid()); - for (MockSecurityRulesVO rule : rules) { - MockVMVO vm = _mockVmDao.findByVmNameAndHost(rule.getVmName(), info.getHostUuid()); - if (vm == null) { - _mockSecurityDao.remove(rule.getId()); - } - } - txn.commit(); - return new Answer(cmd); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("unable to clean up rules", ex); - } finally { - txn.close(); + public Answer CleanupNetworkRules(CleanupNetworkRulesCmd cmd, SimulatorInfo info) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + List rules = _mockSecurityDao.findByHost(info.getHostUuid()); + for (MockSecurityRulesVO rule : rules) { + MockVMVO vm = _mockVmDao.findByVmNameAndHost(rule.getVmName(), info.getHostUuid()); + if (vm == null) { + _mockSecurityDao.remove(rule.getId()); + } + } + txn.commit(); + return new Answer(cmd); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("unable to clean up rules", ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } + } + } @Override - public Answer stopVM(StopCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - String vmName = cmd.getVmName(); - MockVm vm = _mockVmDao.findByVmName(vmName); - if (vm != null) { - vm.setState(State.Stopped); - _mockVmDao.update(vm.getId(), (MockVMVO) vm); - } + public Answer stopVM(StopCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + String vmName = cmd.getVmName(); + MockVm vm = _mockVmDao.findByVmName(vmName); + if (vm != null) { + vm.setState(State.Stopped); + _mockVmDao.update(vm.getId(), (MockVMVO) vm); + } - if (vmName.startsWith("s-")) { - _mockAgentMgr.handleSystemVMStop(vm.getId()); - } - txn.commit(); - return new StopAnswer(cmd, null, new Integer(0), true); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("unable to stop vm " + cmd.getVmName(), ex); - } finally { - txn.close(); + if (vmName.startsWith("s-")) { + _mockAgentMgr.handleSystemVMStop(vm.getId()); + } + txn.commit(); + return new StopAnswer(cmd, null, new Integer(0), true); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("unable to stop vm " + cmd.getVmName(), ex); + } finally { + txn.close(); txn = Transaction.open(Transaction.CLOUD_DB); txn.close(); - } - } + } + } @Override - public Answer rebootVM(RebootCommand cmd) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - MockVm vm = _mockVmDao.findByVmName(cmd.getVmName()); - if (vm != null) { - vm.setState(State.Running); - _mockVmDao.update(vm.getId(), (MockVMVO) vm); - } - txn.commit(); - return new RebootAnswer(cmd, "Rebooted " + cmd.getVmName(), true); - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("unable to stop vm " + cmd.getVmName(), ex); - } finally { - txn.close(); - txn = Transaction.open(Transaction.CLOUD_DB); - txn.close(); - } - } + public Answer rebootVM(RebootCommand cmd) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + MockVm vm = _mockVmDao.findByVmName(cmd.getVmName()); + if (vm != null) { + vm.setState(State.Running); + _mockVmDao.update(vm.getId(), (MockVMVO) vm); + } + txn.commit(); + return new RebootAnswer(cmd, "Rebooted " + cmd.getVmName(), true); + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("unable to stop vm " + cmd.getVmName(), ex); + } finally { + txn.close(); + txn = Transaction.open(Transaction.CLOUD_DB); + txn.close(); + } + } @Override public Answer getVncPort(GetVncPortCommand cmd) { - return new GetVncPortAnswer(cmd, 0); + return new GetVncPortAnswer(cmd, 0); } @Override @@ -546,13 +581,13 @@ public class MockVmManagerImpl implements MockVmManager { @Override public GetDomRVersionAnswer getDomRVersion(GetDomRVersionCmd cmd) { - return new GetDomRVersionAnswer(cmd, null, null, null); + return new GetDomRVersionAnswer(cmd, null, null, null); } @Override public SecurityGroupRuleAnswer AddSecurityGroupRules(SecurityGroupRulesCmd cmd, SimulatorInfo info) { if (!info.isEnabled()) { - return new SecurityGroupRuleAnswer(cmd, false, "Disabled", SecurityGroupRuleAnswer.FailureReason.CANNOT_BRIDGE_FIREWALL); + return new SecurityGroupRuleAnswer(cmd, false, "Disabled", SecurityGroupRuleAnswer.FailureReason.CANNOT_BRIDGE_FIREWALL); } Map> rules = _securityRules.get(info.getHostUuid()); diff --git a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java index 25e7d481a06..b0bc7036e27 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java +++ b/plugins/hypervisors/simulator/src/com/cloud/agent/manager/SimulatorManagerImpl.java @@ -16,30 +16,84 @@ // under the License. package com.cloud.agent.manager; -import com.cloud.agent.api.*; +import java.util.HashMap; +import java.util.Map; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.AttachIsoCommand; +import com.cloud.agent.api.AttachVolumeCommand; +import com.cloud.agent.api.BackupSnapshotCommand; +import com.cloud.agent.api.BumpUpPriorityCommand; +import com.cloud.agent.api.CheckHealthCommand; +import com.cloud.agent.api.CheckNetworkCommand; +import com.cloud.agent.api.CheckRouterCommand; +import com.cloud.agent.api.CheckVirtualMachineCommand; +import com.cloud.agent.api.CleanupNetworkRulesCmd; +import com.cloud.agent.api.ClusterSyncCommand; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.ComputeChecksumCommand; +import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; +import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand; +import com.cloud.agent.api.CreateStoragePoolCommand; +import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; +import com.cloud.agent.api.DeleteSnapshotBackupCommand; +import com.cloud.agent.api.DeleteStoragePoolCommand; +import com.cloud.agent.api.GetDomRVersionCmd; +import com.cloud.agent.api.GetHostStatsCommand; +import com.cloud.agent.api.GetStorageStatsCommand; +import com.cloud.agent.api.GetVmStatsCommand; +import com.cloud.agent.api.GetVncPortCommand; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.ManageSnapshotCommand; +import com.cloud.agent.api.MigrateCommand; +import com.cloud.agent.api.ModifyStoragePoolCommand; +import com.cloud.agent.api.NetworkUsageCommand; +import com.cloud.agent.api.PingTestCommand; +import com.cloud.agent.api.PrepareForMigrationCommand; +import com.cloud.agent.api.RebootCommand; +import com.cloud.agent.api.SecStorageSetupCommand; +import com.cloud.agent.api.SecStorageVMSetupCommand; +import com.cloud.agent.api.SecurityGroupRulesCmd; +import com.cloud.agent.api.StartCommand; +import com.cloud.agent.api.StopCommand; +import com.cloud.agent.api.StoragePoolInfo; import com.cloud.agent.api.check.CheckSshCommand; import com.cloud.agent.api.proxy.CheckConsoleProxyLoadCommand; import com.cloud.agent.api.proxy.WatchConsoleProxyLoadCommand; -import com.cloud.agent.api.routing.*; -import com.cloud.agent.api.storage.*; +import com.cloud.agent.api.routing.DhcpEntryCommand; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.LoadBalancerConfigCommand; +import com.cloud.agent.api.routing.SavePasswordCommand; +import com.cloud.agent.api.routing.SetFirewallRulesCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; +import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.agent.api.storage.CopyVolumeCommand; +import com.cloud.agent.api.storage.CreateCommand; +import com.cloud.agent.api.storage.DeleteTemplateCommand; +import com.cloud.agent.api.storage.DestroyCommand; +import com.cloud.agent.api.storage.DownloadCommand; +import com.cloud.agent.api.storage.DownloadProgressCommand; +import com.cloud.agent.api.storage.ListTemplateCommand; +import com.cloud.agent.api.storage.ListVolumeCommand; +import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; import com.cloud.simulator.MockConfigurationVO; import com.cloud.simulator.MockHost; import com.cloud.simulator.MockVMVO; import com.cloud.simulator.dao.MockConfigurationDao; import com.cloud.simulator.dao.MockHostDao; import com.cloud.utils.Pair; - import com.cloud.utils.db.ConnectionConcierge; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine.State; -import org.apache.log4j.Logger; - -import javax.ejb.Local; -import javax.naming.ConfigurationException; -import java.util.HashMap; -import java.util.Map; @Local(value = { SimulatorManager.class }) public class SimulatorManagerImpl implements SimulatorManager { @@ -57,7 +111,7 @@ public class SimulatorManagerImpl implements SimulatorManager { private ConnectionConcierge _concierge; @Override public boolean configure(String name, Map params) throws ConfigurationException { - /* + /* try { Connection conn = Transaction.getStandaloneSimulatorConnection(); conn.setAutoCommit(true); @@ -65,7 +119,7 @@ public class SimulatorManagerImpl implements SimulatorManager { } catch (SQLException e) { throw new CloudRuntimeException("Unable to get a db connection to simulator", e); } - */ + */ return true; } @@ -146,7 +200,7 @@ public class SimulatorManagerImpl implements SimulatorManager { } else if (cmd instanceof PingTestCommand) { return _mockAgentMgr.pingTest((PingTestCommand) cmd); } else if (cmd instanceof PrepareForMigrationCommand) { - return _mockVmMgr.prepareForMigrate((PrepareForMigrationCommand) cmd); + return _mockVmMgr.prepareForMigrate((PrepareForMigrationCommand) cmd); } else if (cmd instanceof MigrateCommand) { return _mockVmMgr.Migrate((MigrateCommand) cmd, info); } else if (cmd instanceof StartCommand) { @@ -154,11 +208,11 @@ public class SimulatorManagerImpl implements SimulatorManager { } else if (cmd instanceof CheckSshCommand) { return _mockVmMgr.checkSshCommand((CheckSshCommand) cmd); } else if (cmd instanceof CheckVirtualMachineCommand) { - return _mockVmMgr.checkVmState((CheckVirtualMachineCommand) cmd); + return _mockVmMgr.checkVmState((CheckVirtualMachineCommand) cmd); } else if (cmd instanceof SetStaticNatRulesCommand) { return _mockVmMgr.SetStaticNatRules((SetStaticNatRulesCommand) cmd); } else if (cmd instanceof SetFirewallRulesCommand) { - return _mockVmMgr.SetFirewallRules((SetFirewallRulesCommand) cmd); + return _mockVmMgr.SetFirewallRules((SetFirewallRulesCommand) cmd); } else if (cmd instanceof SetPortForwardingRulesCommand) { return _mockVmMgr.SetPortForwardingRules((SetPortForwardingRulesCommand) cmd); } else if (cmd instanceof NetworkUsageCommand) { @@ -174,7 +228,7 @@ public class SimulatorManagerImpl implements SimulatorManager { } else if (cmd instanceof CleanupNetworkRulesCmd) { return _mockVmMgr.CleanupNetworkRules((CleanupNetworkRulesCmd) cmd, info); } else if (cmd instanceof CheckNetworkCommand) { - return _mockAgentMgr.checkNetworkCommand((CheckNetworkCommand) cmd); + return _mockAgentMgr.checkNetworkCommand((CheckNetworkCommand) cmd); }else if (cmd instanceof StopCommand) { return _mockVmMgr.stopVM((StopCommand)cmd); } else if (cmd instanceof RebootCommand) { @@ -244,11 +298,11 @@ public class SimulatorManagerImpl implements SimulatorManager { } else if (cmd instanceof BumpUpPriorityCommand) { return _mockVmMgr.bumpPriority((BumpUpPriorityCommand) cmd); } else if (cmd instanceof GetDomRVersionCmd) { - return _mockVmMgr.getDomRVersion((GetDomRVersionCmd) cmd); + return _mockVmMgr.getDomRVersion((GetDomRVersionCmd) cmd); } else if (cmd instanceof ClusterSyncCommand) { - return new Answer(cmd); + return new Answer(cmd); } else if (cmd instanceof CopyVolumeCommand) { - return _mockStorageMgr.CopyVolume((CopyVolumeCommand) cmd); + return _mockStorageMgr.CopyVolume((CopyVolumeCommand) cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } @@ -270,49 +324,49 @@ public class SimulatorManagerImpl implements SimulatorManager { @Override public Map getVmStates(String hostGuid) { - return _mockVmMgr.getVmStates(hostGuid); + return _mockVmMgr.getVmStates(hostGuid); } @Override public Map getVms(String hostGuid) { - return _mockVmMgr.getVms(hostGuid); + return _mockVmMgr.getVms(hostGuid); } @Override public HashMap> syncNetworkGroups(String hostGuid) { - SimulatorInfo info = new SimulatorInfo(); - info.setHostUuid(hostGuid); - return _mockVmMgr.syncNetworkGroups(info); + SimulatorInfo info = new SimulatorInfo(); + info.setHostUuid(hostGuid); + return _mockVmMgr.syncNetworkGroups(info); } @Override - public boolean configureSimulator(Long zoneId, Long podId, Long clusterId, Long hostId, String command, - String values) { - Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); - try { - txn.start(); - MockConfigurationVO config = _mockConfigDao.findByCommand(zoneId, podId, clusterId, hostId, command); - if (config == null) { - config = new MockConfigurationVO(); - config.setClusterId(clusterId); - config.setDataCenterId(zoneId); - config.setPodId(podId); - config.setHostId(hostId); - config.setName(command); - config.setValues(values); - _mockConfigDao.persist(config); - txn.commit(); - } else { - config.setValues(values); - _mockConfigDao.update(config.getId(), config); - txn.commit(); - } - } catch (Exception ex) { - txn.rollback(); - throw new CloudRuntimeException("Unable to configure simulator because of " + ex.getMessage(), ex); - } finally { - txn.close(); - } - return true; - } + public boolean configureSimulator(Long zoneId, Long podId, Long clusterId, Long hostId, String command, + String values) { + Transaction txn = Transaction.open(Transaction.SIMULATOR_DB); + try { + txn.start(); + MockConfigurationVO config = _mockConfigDao.findByCommand(zoneId, podId, clusterId, hostId, command); + if (config == null) { + config = new MockConfigurationVO(); + config.setClusterId(clusterId); + config.setDataCenterId(zoneId); + config.setPodId(podId); + config.setHostId(hostId); + config.setName(command); + config.setValues(values); + _mockConfigDao.persist(config); + txn.commit(); + } else { + config.setValues(values); + _mockConfigDao.update(config.getId(), config); + txn.commit(); + } + } catch (Exception ex) { + txn.rollback(); + throw new CloudRuntimeException("Unable to configure simulator because of " + ex.getMessage(), ex); + } finally { + txn.close(); + } + return true; + } } diff --git a/plugins/hypervisors/simulator/src/com/cloud/api/commands/ConfigureSimulator.java b/plugins/hypervisors/simulator/src/com/cloud/api/commands/ConfigureSimulator.java index e4e76bb65f9..205484dadd7 100755 --- a/plugins/hypervisors/simulator/src/com/cloud/api/commands/ConfigureSimulator.java +++ b/plugins/hypervisors/simulator/src/com/cloud/api/commands/ConfigureSimulator.java @@ -16,20 +16,21 @@ // under the License. package com.cloud.api.commands; -import org.apache.log4j.Logger; +import javax.inject.Inject; -import com.cloud.agent.manager.SimulatorManager; +import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + +import com.cloud.agent.manager.SimulatorManager; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.server.ManagementService; import com.cloud.user.Account; @@ -38,6 +39,8 @@ public class ConfigureSimulator extends BaseCmd { public static final Logger s_logger = Logger.getLogger(ConfigureSimulator.class.getName()); private static final String s_name = "configuresimulatorresponse"; + @Inject SimulatorManager _simMgr; + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, description="configure range: in a zone") private Long zoneId; @@ -58,8 +61,6 @@ public class ConfigureSimulator extends BaseCmd { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { - ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); - SimulatorManager _simMgr = locator.getManager(SimulatorManager.class); boolean result = _simMgr.configureSimulator(zoneId, podId, clusterId, hostId, command, values); if (!result) { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to configure simulator"); diff --git a/plugins/hypervisors/simulator/src/com/cloud/resource/AgentResourceBase.java b/plugins/hypervisors/simulator/src/com/cloud/resource/AgentResourceBase.java index ee6d8a34825..de3bfd9139a 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/resource/AgentResourceBase.java +++ b/plugins/hypervisors/simulator/src/com/cloud/resource/AgentResourceBase.java @@ -26,6 +26,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; @@ -46,239 +47,237 @@ import com.cloud.simulator.MockHost; public class AgentResourceBase implements ServerResource { - private static final Logger s_logger = Logger - .getLogger(AgentResourceBase.class); + private static final Logger s_logger = Logger.getLogger(AgentResourceBase.class); - protected String _name; - private List _warnings = new LinkedList(); - private List _errors = new LinkedList(); + protected String _name; + private List _warnings = new LinkedList(); + private List _errors = new LinkedList(); - private transient IAgentControl _agentControl; + private transient IAgentControl _agentControl; - protected long _instanceId; + protected long _instanceId; - private Type _type; + private Type _type; - private transient ComponentLocator _locator = null; - protected transient SimulatorManager _simMgr; - protected MockHost agentHost = null; - protected boolean stopped = false; - protected String hostGuid = null; + @Inject protected SimulatorManager _simMgr; + protected MockHost agentHost = null; + protected boolean stopped = false; + protected String hostGuid = null; - public AgentResourceBase(long instanceId, AgentType agentType, SimulatorManager simMgr, String hostGuid) { - _instanceId = instanceId; + public AgentResourceBase(long instanceId, AgentType agentType, SimulatorManager simMgr, String hostGuid) { + _instanceId = instanceId; - if(s_logger.isDebugEnabled()) { - s_logger.info("New Routing host instantiated with guid:" + hostGuid); - } + if(s_logger.isDebugEnabled()) { + s_logger.info("New Routing host instantiated with guid:" + hostGuid); + } - if (agentType == AgentType.Routing) { - _type = Host.Type.Routing; - } else { - _type = Host.Type.Storage; - } + if (agentType == AgentType.Routing) { + _type = Host.Type.Routing; + } else { + _type = Host.Type.Storage; + } - this.hostGuid = hostGuid; - } + this.hostGuid = hostGuid; + } - protected MockVmManager getVmMgr() { - return _simMgr.getVmMgr(); - } + protected MockVmManager getVmMgr() { + return _simMgr.getVmMgr(); + } - protected MockStorageManager getStorageMgr() { - return _simMgr.getStorageMgr(); - } + protected MockStorageManager getStorageMgr() { + return _simMgr.getStorageMgr(); + } - protected MockAgentManager getAgentMgr() { - return _simMgr.getAgentMgr(); - } + protected MockAgentManager getAgentMgr() { + return _simMgr.getAgentMgr(); + } - protected long getInstanceId() { - return _instanceId; - } + protected long getInstanceId() { + return _instanceId; + } - public AgentResourceBase() { - if(s_logger.isDebugEnabled()) { - s_logger.debug("Deserializing simulated agent on reconnect"); - } + public AgentResourceBase() { + if(s_logger.isDebugEnabled()) { + s_logger.debug("Deserializing simulated agent on reconnect"); + } - } + } - @Override - public String getName() { - return _name; - } + @Override + public String getName() { + return _name; + } - public void setName(String name) { - _name = name; - } + public void setName(String name) { + _name = name; + } - @Override - public boolean configure(String name, Map params) - throws ConfigurationException { - hostGuid = (String)params.get("guid"); - _locator = ComponentLocator.getLocator("management-server"); + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + hostGuid = (String)params.get("guid"); + _locator = ComponentLocator.getLocator("management-server"); _simMgr = _locator.getManager(SimulatorManager.class); - agentHost = getAgentMgr().getHost(hostGuid); - return true; - } + agentHost = getAgentMgr().getHost(hostGuid); + return true; + } - private void reconnect(MockHost host) { - if(s_logger.isDebugEnabled()) { - s_logger.debug("Reconfiguring existing simulated host w/ name: " + host.getName() + " and guid: " + host.getGuid()); - } - this.agentHost = host; - } + private void reconnect(MockHost host) { + if(s_logger.isDebugEnabled()) { + s_logger.debug("Reconfiguring existing simulated host w/ name: " + host.getName() + " and guid: " + host.getGuid()); + } + this.agentHost = host; + } - @Override - public void disconnected() { - this.stopped = true; - } + @Override + public void disconnected() { + this.stopped = true; + } - protected void recordWarning(String msg, Throwable th) { - String str = getLogStr(msg, th); - synchronized (_warnings) { - _warnings.add(str); - } - } + protected void recordWarning(String msg, Throwable th) { + String str = getLogStr(msg, th); + synchronized (_warnings) { + _warnings.add(str); + } + } - protected void recordWarning(String msg) { - recordWarning(msg, null); - } + protected void recordWarning(String msg) { + recordWarning(msg, null); + } - protected List getWarnings() { - synchronized (this) { - List results = _warnings; - _warnings = new ArrayList(); - return results; - } - } + protected List getWarnings() { + synchronized (this) { + List results = _warnings; + _warnings = new ArrayList(); + return results; + } + } - protected List getErrors() { - synchronized (this) { - List result = _errors; - _errors = new ArrayList(); - return result; - } - } + protected List getErrors() { + synchronized (this) { + List result = _errors; + _errors = new ArrayList(); + return result; + } + } - protected void recordError(String msg, Throwable th) { - String str = getLogStr(msg, th); - synchronized (_errors) { - _errors.add(str); - } - } + protected void recordError(String msg, Throwable th) { + String str = getLogStr(msg, th); + synchronized (_errors) { + _errors.add(str); + } + } - protected void recordError(String msg) { - recordError(msg, null); - } + protected void recordError(String msg) { + recordError(msg, null); + } - protected Answer createErrorAnswer(Command cmd, String msg, Throwable th) { - StringWriter writer = new StringWriter(); - if (msg != null) { - writer.append(msg); - } - writer.append("===>Stack<==="); - th.printStackTrace(new PrintWriter(writer)); - return new Answer(cmd, false, writer.toString()); - } + protected Answer createErrorAnswer(Command cmd, String msg, Throwable th) { + StringWriter writer = new StringWriter(); + if (msg != null) { + writer.append(msg); + } + writer.append("===>Stack<==="); + th.printStackTrace(new PrintWriter(writer)); + return new Answer(cmd, false, writer.toString()); + } - protected String createErrorDetail(String msg, Throwable th) { - StringWriter writer = new StringWriter(); - if (msg != null) { - writer.append(msg); - } - writer.append("===>Stack<==="); - th.printStackTrace(new PrintWriter(writer)); - return writer.toString(); - } + protected String createErrorDetail(String msg, Throwable th) { + StringWriter writer = new StringWriter(); + if (msg != null) { + writer.append(msg); + } + writer.append("===>Stack<==="); + th.printStackTrace(new PrintWriter(writer)); + return writer.toString(); + } - protected String getLogStr(String msg, Throwable th) { - StringWriter writer = new StringWriter(); - writer.append(new Date().toString()).append(": ").append(msg); - if (th != null) { - writer.append("\n Exception: "); - th.printStackTrace(new PrintWriter(writer)); - } - return writer.toString(); - } + protected String getLogStr(String msg, Throwable th) { + StringWriter writer = new StringWriter(); + writer.append(new Date().toString()).append(": ").append(msg); + if (th != null) { + writer.append("\n Exception: "); + th.printStackTrace(new PrintWriter(writer)); + } + return writer.toString(); + } - @Override - public boolean start() { - return true; - } + @Override + public boolean start() { + return true; + } - @Override - public boolean stop() { - this.stopped = true; - return true; - } + @Override + public boolean stop() { + this.stopped = true; + return true; + } - @Override - public IAgentControl getAgentControl() { - return _agentControl; - } + @Override + public IAgentControl getAgentControl() { + return _agentControl; + } - @Override - public void setAgentControl(IAgentControl agentControl) { - _agentControl = agentControl; - } + @Override + public void setAgentControl(IAgentControl agentControl) { + _agentControl = agentControl; + } - protected String findScript(String script) { - s_logger.debug("Looking for " + script + " in the classpath"); - URL url = ClassLoader.getSystemResource(script); - File file = null; - if (url == null) { - file = new File("./" + script); - s_logger.debug("Looking for " + script + " in " - + file.getAbsolutePath()); - if (!file.exists()) { - return null; - } - } else { - file = new File(url.getFile()); - } - return file.getAbsolutePath(); - } + protected String findScript(String script) { + s_logger.debug("Looking for " + script + " in the classpath"); + URL url = ClassLoader.getSystemResource(script); + File file = null; + if (url == null) { + file = new File("./" + script); + s_logger.debug("Looking for " + script + " in " + + file.getAbsolutePath()); + if (!file.exists()) { + return null; + } + } else { + file = new File(url.getFile()); + } + return file.getAbsolutePath(); + } - @Override - public Answer executeRequest(Command cmd) { - return null; - } + @Override + public Answer executeRequest(Command cmd) { + return null; + } - @Override - public PingCommand getCurrentStatus(long id) { - return null; - } + @Override + public PingCommand getCurrentStatus(long id) { + return null; + } - @Override - public Type getType() { - return _type; - } + @Override + public Type getType() { + return _type; + } - public void setType(Host.Type _type) { - this._type = _type; - } + public void setType(Host.Type _type) { + this._type = _type; + } - @Override - public StartupCommand[] initialize() { - return null; - } + @Override + public StartupCommand[] initialize() { + return null; + } - public SimulatorManager getSimulatorManager() { - return _simMgr; - } + public SimulatorManager getSimulatorManager() { + return _simMgr; + } - public void setSimulatorManager(SimulatorManager simMgr) { - _simMgr = simMgr; - } + public void setSimulatorManager(SimulatorManager simMgr) { + _simMgr = simMgr; + } - public boolean isStopped() { - return this.stopped; - } + public boolean isStopped() { + return this.stopped; + } } diff --git a/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorDiscoverer.java b/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorDiscoverer.java index 69ada1467ef..5cb094184ba 100755 --- a/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorDiscoverer.java +++ b/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorDiscoverer.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.UUID; import javax.ejb.Local; +import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; @@ -43,12 +44,10 @@ import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.exception.ConnectionException; import com.cloud.exception.DiscoveryException; -import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.storage.VMTemplateHostVO; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateZoneVO; import com.cloud.storage.dao.VMTemplateDao; @@ -58,11 +57,11 @@ import com.cloud.storage.dao.VMTemplateZoneDao; @Local(value = Discoverer.class) public class SimulatorDiscoverer extends DiscovererBase implements Discoverer, Listener, ResourceStateAdapter { - private static final Logger s_logger = Logger - .getLogger(SimulatorDiscoverer.class); + private static final Logger s_logger = Logger + .getLogger(SimulatorDiscoverer.class); - @Inject HostDao _hostDao; - @Inject VMTemplateDao _vmTemplateDao; + @Inject HostDao _hostDao; + @Inject VMTemplateDao _vmTemplateDao; @Inject VMTemplateHostDao _vmTemplateHostDao; @Inject VMTemplateZoneDao _vmTemplateZoneDao; @Inject ClusterDao _clusterDao; @@ -71,166 +70,166 @@ public class SimulatorDiscoverer extends DiscovererBase implements Discoverer, L @Inject MockStorageManager _mockStorageMgr = null; @Inject ResourceManager _resourceMgr; - /** - * Finds ServerResources of an in-process simulator - * - * @see com.cloud.resource.Discoverer#find(long, java.lang.Long, - * java.lang.Long, java.net.URI, java.lang.String, java.lang.String) - */ - @Override - public Map> find(long dcId, - Long podId, Long clusterId, URI uri, String username, - String password, List hostTags) throws DiscoveryException { - Map> resources; + /** + * Finds ServerResources of an in-process simulator + * + * @see com.cloud.resource.Discoverer#find(long, java.lang.Long, + * java.lang.Long, java.net.URI, java.lang.String, java.lang.String) + */ + @Override + public Map> find(long dcId, + Long podId, Long clusterId, URI uri, String username, + String password, List hostTags) throws DiscoveryException { + Map> resources; - try { - //http://sim/count=$count, it will add $count number of hosts into the cluster - String scheme = uri.getScheme(); - String host = uri.getAuthority(); - String commands = URLDecoder.decode(uri.getPath()); + try { + //http://sim/count=$count, it will add $count number of hosts into the cluster + String scheme = uri.getScheme(); + String host = uri.getAuthority(); + String commands = URLDecoder.decode(uri.getPath()); - long cpuSpeed = _mockAgentMgr.DEFAULT_HOST_SPEED_MHZ; - long cpuCores = _mockAgentMgr.DEFAULT_HOST_CPU_CORES; - long memory = _mockAgentMgr.DEFAULT_HOST_MEM_SIZE; - long localstorageSize = _mockStorageMgr.DEFAULT_HOST_STORAGE_SIZE; - if (scheme.equals("http")) { - if (host == null || !host.startsWith("sim")) { - String msg = "uri is not of simulator type so we're not taking care of the discovery for this: " - + uri; - if(s_logger.isDebugEnabled()) { - s_logger.debug(msg); - } - return null; - } - if (commands != null) { - int index = commands.lastIndexOf("/"); - if (index != -1) { - commands = commands.substring(index+1); + long cpuSpeed = _mockAgentMgr.DEFAULT_HOST_SPEED_MHZ; + long cpuCores = _mockAgentMgr.DEFAULT_HOST_CPU_CORES; + long memory = _mockAgentMgr.DEFAULT_HOST_MEM_SIZE; + long localstorageSize = _mockStorageMgr.DEFAULT_HOST_STORAGE_SIZE; + if (scheme.equals("http")) { + if (host == null || !host.startsWith("sim")) { + String msg = "uri is not of simulator type so we're not taking care of the discovery for this: " + + uri; + if(s_logger.isDebugEnabled()) { + s_logger.debug(msg); + } + return null; + } + if (commands != null) { + int index = commands.lastIndexOf("/"); + if (index != -1) { + commands = commands.substring(index+1); - String[] cmds = commands.split("&"); - for (String cmd : cmds) { - String[] parameter = cmd.split("="); - if (parameter[0].equalsIgnoreCase("cpuspeed") && parameter[1] != null) { - cpuSpeed = Long.parseLong(parameter[1]); - } else if (parameter[0].equalsIgnoreCase("cpucore") && parameter[1] != null) { - cpuCores = Long.parseLong(parameter[1]); - } else if (parameter[0].equalsIgnoreCase("memory") && parameter[1] != null) { - memory = Long.parseLong(parameter[1]); - } else if (parameter[0].equalsIgnoreCase("localstorage") && parameter[1] != null) { - localstorageSize = Long.parseLong(parameter[1]); - } - } - } - } - } else { - String msg = "uriString is not http so we're not taking care of the discovery for this: " - + uri; - if(s_logger.isDebugEnabled()) { - s_logger.debug(msg); - } - return null; - } + String[] cmds = commands.split("&"); + for (String cmd : cmds) { + String[] parameter = cmd.split("="); + if (parameter[0].equalsIgnoreCase("cpuspeed") && parameter[1] != null) { + cpuSpeed = Long.parseLong(parameter[1]); + } else if (parameter[0].equalsIgnoreCase("cpucore") && parameter[1] != null) { + cpuCores = Long.parseLong(parameter[1]); + } else if (parameter[0].equalsIgnoreCase("memory") && parameter[1] != null) { + memory = Long.parseLong(parameter[1]); + } else if (parameter[0].equalsIgnoreCase("localstorage") && parameter[1] != null) { + localstorageSize = Long.parseLong(parameter[1]); + } + } + } + } + } else { + String msg = "uriString is not http so we're not taking care of the discovery for this: " + + uri; + if(s_logger.isDebugEnabled()) { + s_logger.debug(msg); + } + return null; + } - String cluster = null; - if (clusterId == null) { - String msg = "must specify cluster Id when adding host"; - if(s_logger.isDebugEnabled()) { - s_logger.debug(msg); - } - throw new RuntimeException(msg); - } else { - ClusterVO clu = _clusterDao.findById(clusterId); - if (clu == null - || (clu.getHypervisorType() != HypervisorType.Simulator)) { - if (s_logger.isInfoEnabled()) - s_logger.info("invalid cluster id or cluster is not for Simulator hypervisors"); - return null; - } - cluster = Long.toString(clusterId); - if(clu.getGuid() == null) { - clu.setGuid(UUID.randomUUID().toString()); - } - _clusterDao.update(clusterId, clu); - } + String cluster = null; + if (clusterId == null) { + String msg = "must specify cluster Id when adding host"; + if(s_logger.isDebugEnabled()) { + s_logger.debug(msg); + } + throw new RuntimeException(msg); + } else { + ClusterVO clu = _clusterDao.findById(clusterId); + if (clu == null + || (clu.getHypervisorType() != HypervisorType.Simulator)) { + if (s_logger.isInfoEnabled()) + s_logger.info("invalid cluster id or cluster is not for Simulator hypervisors"); + return null; + } + cluster = Long.toString(clusterId); + if(clu.getGuid() == null) { + clu.setGuid(UUID.randomUUID().toString()); + } + _clusterDao.update(clusterId, clu); + } - String pod; - if (podId == null) { - String msg = "must specify pod Id when adding host"; - if(s_logger.isDebugEnabled()) { - s_logger.debug(msg); - } - throw new RuntimeException(msg); - } else { - pod = Long.toString(podId); - } + String pod; + if (podId == null) { + String msg = "must specify pod Id when adding host"; + if(s_logger.isDebugEnabled()) { + s_logger.debug(msg); + } + throw new RuntimeException(msg); + } else { + pod = Long.toString(podId); + } - Map details = new HashMap(); - Map params = new HashMap(); - details.put("username", username); - params.put("username", username); - details.put("password", password); - params.put("password", password); - params.put("zone", Long.toString(dcId)); - params.put("pod", pod); - params.put("cluster", cluster); - params.put("cpuspeed", Long.toString(cpuSpeed)); - params.put("cpucore", Long.toString(cpuCores)); - params.put("memory", Long.toString(memory)); - params.put("localstorage", Long.toString(localstorageSize)); + Map details = new HashMap(); + Map params = new HashMap(); + details.put("username", username); + params.put("username", username); + details.put("password", password); + params.put("password", password); + params.put("zone", Long.toString(dcId)); + params.put("pod", pod); + params.put("cluster", cluster); + params.put("cpuspeed", Long.toString(cpuSpeed)); + params.put("cpucore", Long.toString(cpuCores)); + params.put("memory", Long.toString(memory)); + params.put("localstorage", Long.toString(localstorageSize)); - resources = createAgentResources(params); - return resources; - } catch (Exception ex) { - s_logger.error("Exception when discovering simulator hosts: " - + ex.getMessage()); - } - return null; - } - - private Map> createAgentResources( - Map params) { - try { - s_logger.info("Creating Simulator Resources"); - return _mockAgentMgr.createServerResources(params); - } catch (Exception ex) { - s_logger.warn("Caught exception at agent resource creation: " - + ex.getMessage(), ex); - } - return null; - } - - @Override - public void postDiscovery(List hosts, long msId) { - - for (HostVO h : hosts) { - associateTemplatesToZone(h.getId(), h.getDataCenterId()); - } - } - - private void associateTemplatesToZone(long hostId, long dcId){ - VMTemplateZoneVO tmpltZone; - - List allTemplates = _vmTemplateDao.listAll(); - for (VMTemplateVO vt: allTemplates){ - if (vt.isCrossZones()) { - tmpltZone = _vmTemplateZoneDao.findByZoneTemplate(dcId, vt.getId()); - if (tmpltZone == null) { - VMTemplateZoneVO vmTemplateZone = new VMTemplateZoneVO(dcId, vt.getId(), new Date()); - _vmTemplateZoneDao.persist(vmTemplateZone); - } - } - } + resources = createAgentResources(params); + return resources; + } catch (Exception ex) { + s_logger.error("Exception when discovering simulator hosts: " + + ex.getMessage()); + } + return null; } - @Override - public HypervisorType getHypervisorType() { - return HypervisorType.Simulator; - } + private Map> createAgentResources( + Map params) { + try { + s_logger.info("Creating Simulator Resources"); + return _mockAgentMgr.createServerResources(params); + } catch (Exception ex) { + s_logger.warn("Caught exception at agent resource creation: " + + ex.getMessage(), ex); + } + return null; + } - @Override - public boolean matchHypervisor(String hypervisor) { - return hypervisor.equalsIgnoreCase(HypervisorType.Simulator.toString()); - } + @Override + public void postDiscovery(List hosts, long msId) { + + for (HostVO h : hosts) { + associateTemplatesToZone(h.getId(), h.getDataCenterId()); + } + } + + private void associateTemplatesToZone(long hostId, long dcId){ + VMTemplateZoneVO tmpltZone; + + List allTemplates = _vmTemplateDao.listAll(); + for (VMTemplateVO vt: allTemplates){ + if (vt.isCrossZones()) { + tmpltZone = _vmTemplateZoneDao.findByZoneTemplate(dcId, vt.getId()); + if (tmpltZone == null) { + VMTemplateZoneVO vmTemplateZone = new VMTemplateZoneVO(dcId, vt.getId(), new Date()); + _vmTemplateZoneDao.persist(vmTemplateZone); + } + } + } + } + + @Override + public HypervisorType getHypervisorType() { + return HypervisorType.Simulator; + } + + @Override + public boolean matchHypervisor(String hypervisor) { + return hypervisor.equalsIgnoreCase(HypervisorType.Simulator.toString()); + } @Override public boolean configure(String name, Map params) throws ConfigurationException { @@ -298,38 +297,38 @@ public class SimulatorDiscoverer extends DiscovererBase implements Discoverer, L return false; } - @Override - public HostVO createHostVOForConnectedAgent(HostVO host, - StartupCommand[] cmd) { - return null; - } + @Override + public HostVO createHostVOForConnectedAgent(HostVO host, + StartupCommand[] cmd) { + return null; + } - @Override - public HostVO createHostVOForDirectConnectAgent(HostVO host, - StartupCommand[] startup, ServerResource resource, - Map details, List hostTags) { - StartupCommand firstCmd = startup[0]; - if (!(firstCmd instanceof StartupRoutingCommand)) { - return null; - } + @Override + public HostVO createHostVOForDirectConnectAgent(HostVO host, + StartupCommand[] startup, ServerResource resource, + Map details, List hostTags) { + StartupCommand firstCmd = startup[0]; + if (!(firstCmd instanceof StartupRoutingCommand)) { + return null; + } - StartupRoutingCommand ssCmd = ((StartupRoutingCommand) firstCmd); - if (ssCmd.getHypervisorType() != HypervisorType.Simulator) { - return null; - } + StartupRoutingCommand ssCmd = ((StartupRoutingCommand) firstCmd); + if (ssCmd.getHypervisorType() != HypervisorType.Simulator) { + return null; + } - return _resourceMgr.fillRoutingHostVO(host, ssCmd, HypervisorType.Simulator, details, hostTags); - } + return _resourceMgr.fillRoutingHostVO(host, ssCmd, HypervisorType.Simulator, details, hostTags); + } - @Override - public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, - boolean isForceDeleteStorage) throws UnableDeleteHostException { - return null; - } + @Override + public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, + boolean isForceDeleteStorage) throws UnableDeleteHostException { + return null; + } @Override public boolean stop() { - _resourceMgr.unregisterResourceStateAdapter(this.getClass().getSimpleName()); + _resourceMgr.unregisterResourceStateAdapter(this.getClass().getSimpleName()); return super.stop(); } diff --git a/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorSecondaryDiscoverer.java b/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorSecondaryDiscoverer.java index 3b22ad51fba..cd0cd2725c9 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorSecondaryDiscoverer.java +++ b/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorSecondaryDiscoverer.java @@ -21,8 +21,11 @@ import java.util.List; import java.util.Map; import javax.ejb.Local; +import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.log4j.Logger; + import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; import com.cloud.agent.api.AgentControlAnswer; @@ -38,9 +41,7 @@ import com.cloud.host.Status; import com.cloud.storage.SnapshotVO; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.secondary.SecondaryStorageDiscoverer; - import com.cloud.utils.exception.CloudRuntimeException; -import org.apache.log4j.Logger; @Local(value=Discoverer.class) public class SimulatorSecondaryDiscoverer extends SecondaryStorageDiscoverer implements ResourceStateAdapter, Listener { @@ -52,7 +53,7 @@ public class SimulatorSecondaryDiscoverer extends SecondaryStorageDiscoverer imp @Override public boolean configure(String name, Map params) throws ConfigurationException { - _agentMgr.registerForHostEvents(this, true, false, false); + _agentMgr.registerForHostEvents(this, true, false, false); _resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this); return super.configure(name, params); } @@ -88,40 +89,40 @@ public class SimulatorSecondaryDiscoverer extends SecondaryStorageDiscoverer imp } } - @Override - public HostVO createHostVOForConnectedAgent(HostVO host, - StartupCommand[] cmd) { - return null; - } + @Override + public HostVO createHostVOForConnectedAgent(HostVO host, + StartupCommand[] cmd) { + return null; + } - @Override - public HostVO createHostVOForDirectConnectAgent(HostVO host, - StartupCommand[] startup, ServerResource resource, - Map details, List hostTags) { - //for detecting SSVM dispatch - StartupCommand firstCmd = startup[0]; - if (!(firstCmd instanceof StartupSecondaryStorageCommand)) { - return null; - } + @Override + public HostVO createHostVOForDirectConnectAgent(HostVO host, + StartupCommand[] startup, ServerResource resource, + Map details, List hostTags) { + //for detecting SSVM dispatch + StartupCommand firstCmd = startup[0]; + if (!(firstCmd instanceof StartupSecondaryStorageCommand)) { + return null; + } - host.setType(com.cloud.host.Host.Type.SecondaryStorageVM); - return host; - } + host.setType(com.cloud.host.Host.Type.SecondaryStorageVM); + return host; + } - @Override - public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, - boolean isForceDeleteStorage) throws UnableDeleteHostException { - long hostId = host.getId(); - List snapshots = _snapshotDao.listByHostId(hostId); - if (snapshots != null && !snapshots.isEmpty()) { - throw new CloudRuntimeException("Cannot delete this secondary storage because there are still snapshots on it "); - } - _vmTemplateHostDao.deleteByHost(hostId); - host.setGuid(null); - _hostDao.update(hostId, host); - _hostDao.remove(hostId); - return new DeleteHostAnswer(true); - } + @Override + public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, + boolean isForceDeleteStorage) throws UnableDeleteHostException { + long hostId = host.getId(); + List snapshots = _snapshotDao.listByHostId(hostId); + if (snapshots != null && !snapshots.isEmpty()) { + throw new CloudRuntimeException("Cannot delete this secondary storage because there are still snapshots on it "); + } + _vmTemplateHostDao.deleteByHost(hostId); + host.setGuid(null); + _hostDao.update(hostId, host); + _hostDao.remove(hostId); + return new DeleteHostAnswer(true); + } @Override public boolean start() { @@ -130,49 +131,49 @@ public class SimulatorSecondaryDiscoverer extends SecondaryStorageDiscoverer imp @Override public boolean stop() { - _resourceMgr.unregisterResourceStateAdapter(this.getClass().getSimpleName()); + _resourceMgr.unregisterResourceStateAdapter(this.getClass().getSimpleName()); return true; } - @Override - public int getTimeout() { - return 0; - } + @Override + public int getTimeout() { + return 0; + } - @Override - public boolean isRecurring() { - return false; - } + @Override + public boolean isRecurring() { + return false; + } - @Override - public boolean processAnswers(long agentId, long seq, Answer[] answers) { - return false; - } + @Override + public boolean processAnswers(long agentId, long seq, Answer[] answers) { + return false; + } - @Override - public boolean processCommands(long agentId, long seq, Command[] commands) { - return false; - } + @Override + public boolean processCommands(long agentId, long seq, Command[] commands) { + return false; + } - @Override - public void processConnect(HostVO host, StartupCommand cmd, - boolean forRebalance) throws ConnectionException { + @Override + public void processConnect(HostVO host, StartupCommand cmd, + boolean forRebalance) throws ConnectionException { - } + } - @Override - public AgentControlAnswer processControlCommand(long agentId, - AgentControlCommand cmd) { - return null; - } + @Override + public AgentControlAnswer processControlCommand(long agentId, + AgentControlCommand cmd) { + return null; + } - @Override - public boolean processDisconnect(long agentId, Status state) { - return false; - } + @Override + public boolean processDisconnect(long agentId, Status state) { + return false; + } - @Override - public boolean processTimeout(long agentId, long seq) { - return false; - } + @Override + public boolean processTimeout(long agentId, long seq) { + return false; + } } diff --git a/plugins/hypervisors/simulator/src/com/cloud/simulator/SimulatorGuru.java b/plugins/hypervisors/simulator/src/com/cloud/simulator/SimulatorGuru.java index 102ce4ec1cc..c9d308023ed 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/simulator/SimulatorGuru.java +++ b/plugins/hypervisors/simulator/src/com/cloud/simulator/SimulatorGuru.java @@ -17,14 +17,14 @@ package com.cloud.simulator; import javax.ejb.Local; +import javax.inject.Inject; import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.HypervisorGuru; import com.cloud.hypervisor.HypervisorGuruBase; -import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.GuestOSVO; import com.cloud.storage.dao.GuestOSDao; - import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; @@ -52,8 +52,8 @@ public class SimulatorGuru extends HypervisorGuruBase implements HypervisorGuru return to; } - @Override - public boolean trackVmHostChange() { - return false; - } + @Override + public boolean trackVmHostChange() { + return false; + } } diff --git a/plugins/hypervisors/simulator/src/com/cloud/simulator/dao/MockVMDaoImpl.java b/plugins/hypervisors/simulator/src/com/cloud/simulator/dao/MockVMDaoImpl.java index e5b30f04c25..be7a98859e2 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/simulator/dao/MockVMDaoImpl.java +++ b/plugins/hypervisors/simulator/src/com/cloud/simulator/dao/MockVMDaoImpl.java @@ -21,11 +21,11 @@ import java.util.List; import java.util.Map; import javax.ejb.Local; +import javax.inject.Inject; import javax.naming.ConfigurationException; import com.cloud.simulator.MockHostVO; import com.cloud.simulator.MockVMVO; - import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; diff --git a/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java b/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java index 28ed92ded0c..1b29f69794a 100644 --- a/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java +++ b/plugins/user-authenticators/sha256salted/src/com/cloud/server/auth/SHA256SaltedUserAuthenticator.java @@ -23,40 +23,38 @@ import java.security.SecureRandom; import java.util.Map; import javax.ejb.Local; +import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import org.bouncycastle.util.encoders.Base64; -import com.cloud.server.ManagementServer; -import com.cloud.servlet.CloudStartupServlet; import com.cloud.user.UserAccount; import com.cloud.user.dao.UserAccountDao; - - import com.cloud.utils.exception.CloudRuntimeException; @Local(value={UserAuthenticator.class}) public class SHA256SaltedUserAuthenticator extends DefaultUserAuthenticator { - public static final Logger s_logger = Logger.getLogger(SHA256SaltedUserAuthenticator.class); - - @Inject - private UserAccountDao _userAccountDao; - private static int s_saltlen = 20; + public static final Logger s_logger = Logger.getLogger(SHA256SaltedUserAuthenticator.class); - public boolean configure(String name, Map params) - throws ConfigurationException { - super.configure(name, params); - return true; - } - - /* (non-Javadoc) - * @see com.cloud.server.auth.UserAuthenticator#authenticate(java.lang.String, java.lang.String, java.lang.Long, java.util.Map) - */ - @Override - public boolean authenticate(String username, String password, - Long domainId, Map requestParameters) { - if (s_logger.isDebugEnabled()) { + @Inject + private UserAccountDao _userAccountDao; + private static int s_saltlen = 20; + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + super.configure(name, params); + return true; + } + + /* (non-Javadoc) + * @see com.cloud.server.auth.UserAuthenticator#authenticate(java.lang.String, java.lang.String, java.lang.Long, java.util.Map) + */ + @Override + public boolean authenticate(String username, String password, + Long domainId, Map requestParameters) { + if (s_logger.isDebugEnabled()) { s_logger.debug("Retrieving user: " + username); } UserAccount user = _userAccountDao.getUserAccount(username, domainId); @@ -64,59 +62,59 @@ public class SHA256SaltedUserAuthenticator extends DefaultUserAuthenticator { s_logger.debug("Unable to find user with " + username + " in domain " + domainId); return false; } - + try { - String storedPassword[] = user.getPassword().split(":"); - if (storedPassword.length != 2) { - s_logger.warn("The stored password for " + username + " isn't in the right format for this authenticator"); - return false; - } - byte salt[] = Base64.decode(storedPassword[0]); - String hashedPassword = encode(password, salt); - return storedPassword[1].equals(hashedPassword); - } catch (NoSuchAlgorithmException e) { - throw new CloudRuntimeException("Unable to hash password", e); - } catch (UnsupportedEncodingException e) { - throw new CloudRuntimeException("Unable to hash password", e); - } - } + String storedPassword[] = user.getPassword().split(":"); + if (storedPassword.length != 2) { + s_logger.warn("The stored password for " + username + " isn't in the right format for this authenticator"); + return false; + } + byte salt[] = Base64.decode(storedPassword[0]); + String hashedPassword = encode(password, salt); + return storedPassword[1].equals(hashedPassword); + } catch (NoSuchAlgorithmException e) { + throw new CloudRuntimeException("Unable to hash password", e); + } catch (UnsupportedEncodingException e) { + throw new CloudRuntimeException("Unable to hash password", e); + } + } - /* (non-Javadoc) - * @see com.cloud.server.auth.UserAuthenticator#encode(java.lang.String) - */ - @Override - public String encode(String password) { - // 1. Generate the salt - SecureRandom randomGen; - try { - randomGen = SecureRandom.getInstance("SHA1PRNG"); - - byte salt[] = new byte[s_saltlen]; - randomGen.nextBytes(salt); - - String saltString = new String(Base64.encode(salt)); - String hashString = encode(password, salt); - - // 3. concatenate the two and return - return saltString + ":" + hashString; - } catch (NoSuchAlgorithmException e) { - throw new CloudRuntimeException("Unable to hash password", e); - } catch (UnsupportedEncodingException e) { - throw new CloudRuntimeException("Unable to hash password", e); - } - } + /* (non-Javadoc) + * @see com.cloud.server.auth.UserAuthenticator#encode(java.lang.String) + */ + @Override + public String encode(String password) { + // 1. Generate the salt + SecureRandom randomGen; + try { + randomGen = SecureRandom.getInstance("SHA1PRNG"); - public String encode(String password, byte[] salt) throws UnsupportedEncodingException, NoSuchAlgorithmException { - byte[] passwordBytes = password.getBytes("UTF-8"); - byte[] hashSource = new byte[passwordBytes.length + s_saltlen]; - System.arraycopy(passwordBytes, 0, hashSource, 0, passwordBytes.length); - System.arraycopy(salt, 0, hashSource, passwordBytes.length, s_saltlen); - - // 2. Hash the password with the salt - MessageDigest md = MessageDigest.getInstance("SHA-256"); - md.update(hashSource); - byte[] digest = md.digest(); - - return new String(Base64.encode(digest)); - } + byte salt[] = new byte[s_saltlen]; + randomGen.nextBytes(salt); + + String saltString = new String(Base64.encode(salt)); + String hashString = encode(password, salt); + + // 3. concatenate the two and return + return saltString + ":" + hashString; + } catch (NoSuchAlgorithmException e) { + throw new CloudRuntimeException("Unable to hash password", e); + } catch (UnsupportedEncodingException e) { + throw new CloudRuntimeException("Unable to hash password", e); + } + } + + public String encode(String password, byte[] salt) throws UnsupportedEncodingException, NoSuchAlgorithmException { + byte[] passwordBytes = password.getBytes("UTF-8"); + byte[] hashSource = new byte[passwordBytes.length + s_saltlen]; + System.arraycopy(passwordBytes, 0, hashSource, 0, passwordBytes.length); + System.arraycopy(salt, 0, hashSource, passwordBytes.length, s_saltlen); + + // 2. Hash the password with the salt + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(hashSource); + byte[] digest = md.digest(); + + return new String(Base64.encode(digest)); + } } diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index cbcdccb5f64..592a7ad861d 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -20,7 +20,6 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URLDecoder; import java.util.ArrayList; -import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -32,16 +31,20 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd; +import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd; +import org.apache.cloudstack.api.command.admin.host.AddHostCmd; +import org.apache.cloudstack.api.command.admin.host.AddSecondaryStorageCmd; +import org.apache.cloudstack.api.command.admin.host.CancelMaintenanceCmd; +import org.apache.cloudstack.api.command.admin.host.PrepareForMaintenanceCmd; +import org.apache.cloudstack.api.command.admin.host.ReconnectHostCmd; +import org.apache.cloudstack.api.command.admin.host.UpdateHostCmd; +import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd; +import org.apache.cloudstack.api.command.admin.storage.AddS3Cmd; import org.apache.cloudstack.api.command.admin.storage.ListS3sCmd; import org.apache.cloudstack.api.command.admin.swift.AddSwiftCmd; -import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd; -import org.apache.cloudstack.api.command.admin.host.*; import org.apache.cloudstack.api.command.admin.swift.ListSwiftsCmd; -import org.apache.cloudstack.api.command.admin.storage.AddS3Cmd; -import com.cloud.storage.S3; -import com.cloud.storage.S3VO; -import com.cloud.storage.s3.S3Manager; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -59,12 +62,7 @@ import com.cloud.agent.api.UpdateHostPasswordCommand; import com.cloud.agent.manager.AgentAttache; import com.cloud.agent.manager.allocator.PodAllocator; import com.cloud.agent.transport.Request; -import org.apache.cloudstack.api.ApiConstants; import com.cloud.api.ApiDBUtils; -import org.apache.cloudstack.api.command.admin.host.AddHostCmd; -import org.apache.cloudstack.api.command.admin.host.CancelMaintenanceCmd; -import org.apache.cloudstack.api.command.admin.host.PrepareForMaintenanceCmd; -import org.apache.cloudstack.api.command.admin.host.UpdateHostCmd; import com.cloud.capacity.Capacity; import com.cloud.capacity.CapacityVO; import com.cloud.capacity.dao.CapacityDao; @@ -112,6 +110,8 @@ import com.cloud.org.Grouping.AllocationState; import com.cloud.org.Managed; import com.cloud.service.ServiceOfferingVO; import com.cloud.storage.GuestOSCategoryVO; +import com.cloud.storage.S3; +import com.cloud.storage.S3VO; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolHostVO; @@ -125,6 +125,7 @@ import com.cloud.storage.dao.GuestOSCategoryDao; import com.cloud.storage.dao.StoragePoolDao; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.storage.s3.S3Manager; import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.storage.swift.SwiftManager; import com.cloud.template.VirtualMachineTemplate; @@ -135,7 +136,6 @@ import com.cloud.user.UserContext; import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; import com.cloud.utils.UriUtils; -import com.cloud.utils.component.Adapters; import com.cloud.utils.component.Manager; import com.cloud.utils.db.DB; import com.cloud.utils.db.SearchCriteria; @@ -219,7 +219,7 @@ public class ResourceManagerImpl implements ResourceManager, ResourceService, Ma // @com.cloud.utils.component.Inject(adapter = PodAllocator.class) @Inject protected List _podAllocators = null; - + @Inject protected VMTemplateDao _templateDao; @Inject @@ -236,9 +236,9 @@ public class ResourceManagerImpl implements ResourceManager, ResourceService, Ma @PostConstruct public void init() { - // TODO initialize pod allocators here instead + // TODO initialize pod allocators here instead } - + private void insertListener(Integer event, ResourceListener listener) { List lst = _lifeCycleListeners.get(event); if (lst == null) { @@ -510,10 +510,10 @@ public class ResourceManagerImpl implements ResourceManager, ResourceService, Ma @Override public Discoverer getMatchingDiscover(Hypervisor.HypervisorType hypervisorType) { - for(Discoverer discoverer : _discoverers) { + for(Discoverer discoverer : _discoverers) { if (discoverer.getHypervisorType() == hypervisorType) return discoverer; - } + } return null; } @@ -1629,7 +1629,7 @@ public class ResourceManagerImpl implements ResourceManager, ResourceService, Ma } } } - + if (s_logger.isDebugEnabled()) { new Request(-1l, -1l, cmds, true, false).logD("Startup request from directly connected host: ", true); } @@ -1670,7 +1670,7 @@ public class ResourceManagerImpl implements ResourceManager, ResourceService, Ma } } } - + if (tempHost != null) { /* Change agent status to Alert */ _agentMgr.agentStatusTransitTo(tempHost, Status.Event.AgentDisconnected, _nodeId); @@ -2179,7 +2179,7 @@ public class ResourceManagerImpl implements ResourceManager, ResourceService, Ma @Override public Pair findPod(VirtualMachineTemplate template, ServiceOfferingVO offering, DataCenterVO dc, long accountId, Set avoids) { - for(PodAllocator allocator : _podAllocators) { + for(PodAllocator allocator : _podAllocators) { final Pair pod = allocator.allocateTo(template, offering, dc, accountId, avoids); if (pod != null) { return pod; diff --git a/server/src/com/cloud/servlet/ConsoleProxyServlet.java b/server/src/com/cloud/servlet/ConsoleProxyServlet.java index c1001da20d6..568ad0638c6 100644 --- a/server/src/com/cloud/servlet/ConsoleProxyServlet.java +++ b/server/src/com/cloud/servlet/ConsoleProxyServlet.java @@ -27,15 +27,16 @@ import java.util.Map; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; +import javax.inject.Inject; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import org.apache.cloudstack.api.IdentityService; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; -import org.apache.cloudstack.api.IdentityService; import com.cloud.exception.PermissionDeniedException; import com.cloud.host.HostVO; import com.cloud.server.ManagementServer; @@ -46,7 +47,6 @@ import com.cloud.user.User; import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; - import com.cloud.utils.db.Transaction; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; @@ -63,10 +63,10 @@ public class ConsoleProxyServlet extends HttpServlet { private static final int DEFAULT_THUMBNAIL_WIDTH = 144; private static final int DEFAULT_THUMBNAIL_HEIGHT = 110; - private final static AccountManager _accountMgr = ComponentLocator.getLocator(ManagementServer.Name).getManager(AccountManager.class); - private final static VirtualMachineManager _vmMgr = ComponentLocator.getLocator(ManagementServer.Name).getManager(VirtualMachineManager.class); - private final static ManagementServer _ms = (ManagementServer)ComponentLocator.getComponent(ManagementServer.Name); - private final static IdentityService _identityService = ComponentLocator.getLocator(ManagementServer.Name).getManager(IdentityService.class); + @Inject AccountManager _accountMgr; + @Inject VirtualMachineManager _vmMgr; + @Inject ManagementServer _ms; + @Inject IdentityService _identityService; @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) { diff --git a/server/src/com/cloud/servlet/RegisterCompleteServlet.java b/server/src/com/cloud/servlet/RegisterCompleteServlet.java index d159c887fab..aecbce39f92 100644 --- a/server/src/com/cloud/servlet/RegisterCompleteServlet.java +++ b/server/src/com/cloud/servlet/RegisterCompleteServlet.java @@ -19,9 +19,9 @@ package com.cloud.servlet; import java.net.URLEncoder; import java.util.List; +import javax.inject.Inject; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; -import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -30,7 +30,6 @@ import org.apache.log4j.Logger; import com.cloud.configuration.Configuration; import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.server.ManagementServer; import com.cloud.user.Account; import com.cloud.user.AccountService; import com.cloud.user.User; @@ -40,104 +39,90 @@ import com.cloud.utils.SerialVersionUID; public class RegisterCompleteServlet extends HttpServlet implements ServletContextListener { - public static final Logger s_logger = Logger.getLogger(RegisterCompleteServlet.class.getName()); - + public static final Logger s_logger = Logger.getLogger(RegisterCompleteServlet.class.getName()); + static final long serialVersionUID = SerialVersionUID.CloudStartupServlet; - - protected static AccountService _accountSvc = null; - protected static ConfigurationDao _configDao = null; - protected static UserDao _userDao = null; - - @Override - public void init() throws ServletException { - ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); - _accountSvc = locator.getManager(AccountService.class); - _configDao = locator.getDao(ConfigurationDao.class); - _userDao = locator.getDao(UserDao.class); - } - - @Override - public void contextInitialized(ServletContextEvent sce) { - try { - init(); - } catch (ServletException e) { - s_logger.error("Exception starting management server ", e); - throw new RuntimeException(e); - } - } - - @Override - public void contextDestroyed(ServletContextEvent sce) { - } - - @Override + + @Inject AccountService _accountSvc = null; + @Inject ConfigurationDao _configDao = null; + @Inject UserDao _userDao = null; + + @Override + public void contextInitialized(ServletContextEvent sce) { + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + } + + @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) { - doGet(req, resp); - } - - @Override + doGet(req, resp); + } + + @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { - String registrationToken = req.getParameter("token"); - String expires = req.getParameter("expires"); - int statusCode = HttpServletResponse.SC_OK; - String responseMessage = null; - - if (registrationToken == null || registrationToken.trim().length() == 0) { - statusCode = 503; - responseMessage = "{ \"registration_info\" : { \"errorcode\" : \"503\", \"errortext\" : \"Missing token\" } }"; - } else { - s_logger.info("Attempting to register user account with token = "+registrationToken); - User resourceAdminUser = _accountSvc.getActiveUserByRegistrationToken(registrationToken); - if (resourceAdminUser != null) { - if(resourceAdminUser.isRegistered()) { - statusCode = 503; - responseMessage = "{ \"registration_info\" : { \"errorcode\" : \"503\", \"errortext\" : \"Expired token = " + registrationToken + "\" } }"; - } else { - if(expires != null && expires.toLowerCase().equals("true")){ - _accountSvc.markUserRegistered(resourceAdminUser.getId()); - } - - Account resourceAdminAccount = _accountSvc.getActiveAccountById(resourceAdminUser.getAccountId()); - Account rsUserAccount = _accountSvc.getActiveAccountByName(resourceAdminAccount.getAccountName()+"-user", resourceAdminAccount.getDomainId()); - - List users = _userDao.listByAccount(rsUserAccount.getId()); - User rsUser = users.get(0); - - Configuration config = _configDao.findByName("endpointe.url"); - - StringBuffer sb = new StringBuffer(); - sb.append("{ \"registration_info\" : { \"endpoint_url\" : \""+encodeParam(config.getValue())+"\", "); - sb.append("\"domain_id\" : \""+resourceAdminAccount.getDomainId()+"\", "); - sb.append("\"admin_account\" : \""+encodeParam(resourceAdminUser.getUsername())+"\", "); - sb.append("\"admin_account_api_key\" : \""+resourceAdminUser.getApiKey()+"\", "); - sb.append("\"admin_account_secret_key\" : \""+resourceAdminUser.getSecretKey()+"\", "); - sb.append("\"user_account\" : \""+encodeParam(rsUser.getUsername())+"\", "); - sb.append("\"user_account_api_key\" : \""+rsUser.getApiKey()+"\", "); - sb.append("\"user_account_secret_key\" : \""+rsUser.getSecretKey()+"\" "); - sb.append("} }"); - responseMessage = sb.toString(); - } - } else { - statusCode = 503; - responseMessage = "{ \"registration_info\" : { \"errorcode\" : \"503\", \"errortext\" : \"Invalid token = " + registrationToken + "\" } }"; - } - } - - try { - resp.setContentType("text/javascript; charset=UTF-8"); - resp.setStatus(statusCode); - resp.getWriter().print(responseMessage); - } catch (Exception ex) { - s_logger.error("unknown exception writing register complete response", ex); + String registrationToken = req.getParameter("token"); + String expires = req.getParameter("expires"); + int statusCode = HttpServletResponse.SC_OK; + String responseMessage = null; + + if (registrationToken == null || registrationToken.trim().length() == 0) { + statusCode = 503; + responseMessage = "{ \"registration_info\" : { \"errorcode\" : \"503\", \"errortext\" : \"Missing token\" } }"; + } else { + s_logger.info("Attempting to register user account with token = "+registrationToken); + User resourceAdminUser = _accountSvc.getActiveUserByRegistrationToken(registrationToken); + if (resourceAdminUser != null) { + if(resourceAdminUser.isRegistered()) { + statusCode = 503; + responseMessage = "{ \"registration_info\" : { \"errorcode\" : \"503\", \"errortext\" : \"Expired token = " + registrationToken + "\" } }"; + } else { + if(expires != null && expires.toLowerCase().equals("true")){ + _accountSvc.markUserRegistered(resourceAdminUser.getId()); + } + + Account resourceAdminAccount = _accountSvc.getActiveAccountById(resourceAdminUser.getAccountId()); + Account rsUserAccount = _accountSvc.getActiveAccountByName(resourceAdminAccount.getAccountName()+"-user", resourceAdminAccount.getDomainId()); + + List users = _userDao.listByAccount(rsUserAccount.getId()); + User rsUser = users.get(0); + + Configuration config = _configDao.findByName("endpointe.url"); + + StringBuffer sb = new StringBuffer(); + sb.append("{ \"registration_info\" : { \"endpoint_url\" : \""+encodeParam(config.getValue())+"\", "); + sb.append("\"domain_id\" : \""+resourceAdminAccount.getDomainId()+"\", "); + sb.append("\"admin_account\" : \""+encodeParam(resourceAdminUser.getUsername())+"\", "); + sb.append("\"admin_account_api_key\" : \""+resourceAdminUser.getApiKey()+"\", "); + sb.append("\"admin_account_secret_key\" : \""+resourceAdminUser.getSecretKey()+"\", "); + sb.append("\"user_account\" : \""+encodeParam(rsUser.getUsername())+"\", "); + sb.append("\"user_account_api_key\" : \""+rsUser.getApiKey()+"\", "); + sb.append("\"user_account_secret_key\" : \""+rsUser.getSecretKey()+"\" "); + sb.append("} }"); + responseMessage = sb.toString(); + } + } else { + statusCode = 503; + responseMessage = "{ \"registration_info\" : { \"errorcode\" : \"503\", \"errortext\" : \"Invalid token = " + registrationToken + "\" } }"; + } } - } - - private String encodeParam(String value) { - try { - return URLEncoder.encode(value, "UTF-8").replaceAll("\\+", "%20"); - } catch (Exception e) { - s_logger.warn("Unable to encode: " + value); - } - return value; - } + + try { + resp.setContentType("text/javascript; charset=UTF-8"); + resp.setStatus(statusCode); + resp.getWriter().print(responseMessage); + } catch (Exception ex) { + s_logger.error("unknown exception writing register complete response", ex); + } + } + + private String encodeParam(String value) { + try { + return URLEncoder.encode(value, "UTF-8").replaceAll("\\+", "%20"); + } catch (Exception e) { + s_logger.warn("Unable to encode: " + value); + } + return value; + } } diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 602d10d19c3..bff76b98da1 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -28,7 +28,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; -import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -45,7 +44,10 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.cloudstack.api.command.admin.storage.*; +import org.apache.cloudstack.api.command.admin.storage.CancelPrimaryStorageMaintenanceCmd; +import org.apache.cloudstack.api.command.admin.storage.CreateStoragePoolCmd; +import org.apache.cloudstack.api.command.admin.storage.DeletePoolCmd; +import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd; import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd; import org.apache.log4j.Logger; @@ -76,14 +78,12 @@ import com.cloud.agent.api.to.VolumeTO; import com.cloud.agent.manager.Commands; import com.cloud.alert.AlertManager; import com.cloud.api.ApiDBUtils; -import org.apache.cloudstack.api.command.admin.storage.CreateStoragePoolCmd; import com.cloud.async.AsyncJobManager; import com.cloud.capacity.Capacity; import com.cloud.capacity.CapacityManager; import com.cloud.capacity.CapacityState; import com.cloud.capacity.CapacityVO; import com.cloud.capacity.dao.CapacityDao; -import com.cloud.cluster.CheckPointManager; import com.cloud.cluster.ClusterManagerListener; import com.cloud.cluster.ManagementServerHostVO; import com.cloud.configuration.Config; @@ -172,8 +172,7 @@ import com.cloud.utils.EnumUtils; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.UriUtils; -import com.cloud.utils.component.Adapters; - +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; @@ -322,8 +321,6 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag @Inject protected ResourceManager _resourceMgr; @Inject - protected CheckPointManager _checkPointMgr; - @Inject protected DownloadMonitor _downloadMonitor; @Inject protected ResourceTagDao _resourceTagDao; @@ -355,14 +352,14 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag protected BigDecimal _overProvisioningFactor = new BigDecimal(1); private long _maxVolumeSizeInGb; private long _serverId; - private StateMachine2 _volStateMachine; + private final StateMachine2 _volStateMachine; private int _customDiskOfferingMinSize = 1; private int _customDiskOfferingMaxSize = 1024; private double _storageUsedThreshold = 1.0d; private double _storageAllocatedThreshold = 1.0d; protected BigDecimal _storageOverprovisioningFactor = new BigDecimal(1); - private boolean _recreateSystemVmEnabled; + private boolean _recreateSystemVmEnabled; public boolean share(VMInstanceVO vm, List vols, HostVO host, boolean cancelPreviousShare) throws StorageUnavailableException { @@ -653,9 +650,9 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag Pair volumeDetails = createVolumeFromSnapshot(volume, snapshot); if (volumeDetails != null) { createdVolume = volumeDetails.first(); - UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, createdVolume.getAccountId(), createdVolume.getDataCenterId(), createdVolume.getId(), createdVolume.getName(), - createdVolume.getDiskOfferingId(), null, createdVolume.getSize()); - _usageEventDao.persist(usageEvent); + UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, createdVolume.getAccountId(), createdVolume.getDataCenterId(), createdVolume.getId(), createdVolume.getName(), + createdVolume.getDiskOfferingId(), null, createdVolume.getSize()); + _usageEventDao.persist(usageEvent); } return createdVolume; } @@ -739,32 +736,32 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag @DB public VolumeVO copyVolumeFromSecToPrimary(VolumeVO volume, VMInstanceVO vm, VMTemplateVO template, DataCenterVO dc, HostPodVO pod, Long clusterId, ServiceOfferingVO offering, DiskOfferingVO diskOffering, List avoids, long size, HypervisorType hyperType) throws NoTransitionException { - - final HashSet avoidPools = new HashSet(avoids); - DiskProfile dskCh = createDiskCharacteristics(volume, template, dc, diskOffering); - dskCh.setHyperType(vm.getHypervisorType()); - // Find a suitable storage to create volume on - StoragePoolVO destPool = findStoragePool(dskCh, dc, pod, clusterId, null, vm, avoidPools); - - // Copy the volume from secondary storage to the destination storage pool - stateTransitTo(volume, Event.CopyRequested); - VolumeHostVO volumeHostVO = _volumeHostDao.findByVolumeId(volume.getId()); - HostVO secStorage = _hostDao.findById(volumeHostVO.getHostId()); - String secondaryStorageURL = secStorage.getStorageUrl(); - String[] volumePath = volumeHostVO.getInstallPath().split("/"); - String volumeUUID = volumePath[volumePath.length - 1].split("\\.")[0]; - + + final HashSet avoidPools = new HashSet(avoids); + DiskProfile dskCh = createDiskCharacteristics(volume, template, dc, diskOffering); + dskCh.setHyperType(vm.getHypervisorType()); + // Find a suitable storage to create volume on + StoragePoolVO destPool = findStoragePool(dskCh, dc, pod, clusterId, null, vm, avoidPools); + + // Copy the volume from secondary storage to the destination storage pool + stateTransitTo(volume, Event.CopyRequested); + VolumeHostVO volumeHostVO = _volumeHostDao.findByVolumeId(volume.getId()); + HostVO secStorage = _hostDao.findById(volumeHostVO.getHostId()); + String secondaryStorageURL = secStorage.getStorageUrl(); + String[] volumePath = volumeHostVO.getInstallPath().split("/"); + String volumeUUID = volumePath[volumePath.length - 1].split("\\.")[0]; + CopyVolumeCommand cvCmd = new CopyVolumeCommand(volume.getId(), volumeUUID, destPool, secondaryStorageURL, false, _copyvolumewait); CopyVolumeAnswer cvAnswer; - try { + try { cvAnswer = (CopyVolumeAnswer) sendToPool(destPool, cvCmd); } catch (StorageUnavailableException e1) { - stateTransitTo(volume, Event.CopyFailed); + stateTransitTo(volume, Event.CopyFailed); throw new CloudRuntimeException("Failed to copy the volume from secondary storage to the destination primary storage pool."); } if (cvAnswer == null || !cvAnswer.getResult()) { - stateTransitTo(volume, Event.CopyFailed); + stateTransitTo(volume, Event.CopyFailed); throw new CloudRuntimeException("Failed to copy the volume from secondary storage to the destination primary storage pool."); } Transaction txn = Transaction.currentTxn(); @@ -778,11 +775,11 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), volume.getDiskOfferingId(), null, volume.getSize()); _usageEventDao.persist(usageEvent); _volumeHostDao.remove(volumeHostVO.getId()); - txn.commit(); - return volume; - + txn.commit(); + return volume; + } - + @Override @DB public VolumeVO createVolume(VolumeVO volume, VMInstanceVO vm, VMTemplateVO template, DataCenterVO dc, HostPodVO pod, Long clusterId, ServiceOfferingVO offering, DiskOfferingVO diskOffering, @@ -848,11 +845,11 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag String fullTmpltUrl = tmpltHostUrl + "/" + tmpltHostOn.getInstallPath(); cmd = new CreateCommand(dskCh, fullTmpltUrl, new StorageFilerTO(pool)); } else { - tmpltStoredOn = _tmpltMgr.prepareTemplateForCreate(template, pool); - if (tmpltStoredOn == null) { - continue; - } - cmd = new CreateCommand(dskCh, tmpltStoredOn.getLocalDownloadPath(), new StorageFilerTO(pool)); + tmpltStoredOn = _tmpltMgr.prepareTemplateForCreate(template, pool); + if (tmpltStoredOn == null) { + continue; + } + cmd = new CreateCommand(dskCh, tmpltStoredOn.getLocalDownloadPath(), new StorageFilerTO(pool)); } } else { if (volume.getVolumeType() == Type.ROOT && Storage.ImageFormat.ISO == template.getFormat()) { @@ -969,7 +966,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag value = _configDao.getValue(Config.RecreateSystemVmEnabled.key()); _recreateSystemVmEnabled = Boolean.parseBoolean(value); - + value = _configDao.getValue(Config.StorageTemplateCleanupEnabled.key()); _templateCleanupEnabled = (value == null ? true : Boolean.parseBoolean(value)); @@ -995,7 +992,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag int wrks = NumbersUtil.parseInt(workers, 10); _executor = Executors.newScheduledThreadPool(wrks, new NamedThreadFactory("StorageManager-Scavenger")); - _agentMgr.registerForHostEvents(ComponentLocator.inject(LocalStoragePoolListener.class), true, false, false); + _agentMgr.registerForHostEvents(ComponentContext.inject(LocalStoragePoolListener.class), true, false, false); String maxVolumeSizeInGbString = _configDao.getValue("storage.max.volume.size"); _maxVolumeSizeInGb = NumbersUtil.parseLong(maxVolumeSizeInGbString, 2000); @@ -1536,10 +1533,10 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag // If it does , then you cannot delete the pool if (vlms.first() > 0) { throw new CloudRuntimeException("Cannot delete pool " + sPool.getName() + " as there are associated vols" + - " for this pool"); + " for this pool"); } } - + // First get the host_id from storage_pool_host_ref for given pool id StoragePoolVO lock = _storagePoolDao.acquireInLockTable(sPool.getId()); @@ -1733,7 +1730,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag return _volsDao.findById(volume.getId()); } - + /* * Upload the volume to secondary storage. * @@ -1742,19 +1739,19 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag @DB @ActionEvent(eventType = EventTypes.EVENT_VOLUME_UPLOAD, eventDescription = "uploading volume", async = true) public VolumeVO uploadVolume(UploadVolumeCmd cmd) throws ResourceAllocationException{ - Account caller = UserContext.current().getCaller(); + Account caller = UserContext.current().getCaller(); long ownerId = cmd.getEntityOwnerId(); Long zoneId = cmd.getZoneId(); String volumeName = cmd.getVolumeName(); String url = cmd.getUrl(); String format = cmd.getFormat(); - - validateVolume(caller, ownerId, zoneId, volumeName, url, format); - VolumeVO volume = persistVolume(caller, ownerId, zoneId, volumeName, url, cmd.getFormat()); - _downloadMonitor.downloadVolumeToStorage(volume, zoneId, url, cmd.getChecksum(), ImageFormat.valueOf(format.toUpperCase())); - return volume; + + validateVolume(caller, ownerId, zoneId, volumeName, url, format); + VolumeVO volume = persistVolume(caller, ownerId, zoneId, volumeName, url, cmd.getFormat()); + _downloadMonitor.downloadVolumeToStorage(volume, zoneId, url, cmd.getChecksum(), ImageFormat.valueOf(format.toUpperCase())); + return volume; } - + private boolean validateVolume(Account caller, long ownerId, Long zoneId, String volumeName, String url, String format) throws ResourceAllocationException{ // permission check @@ -1762,7 +1759,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag // Check that the resource limit for volumes won't be exceeded _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(ownerId), ResourceType.volume); - + // Verify that zone exists DataCenterVO zone = _dcDao.findById(zoneId); @@ -1774,75 +1771,75 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zoneId); } - - if (url.toLowerCase().contains("file://")) { - throw new InvalidParameterValueException("File:// type urls are currently unsupported"); - } - - ImageFormat imgfmt = ImageFormat.valueOf(format.toUpperCase()); - if (imgfmt == null) { - throw new IllegalArgumentException("Image format is incorrect " + format + ". Supported formats are " + EnumUtils.listValues(ImageFormat.values())); - } - + + if (url.toLowerCase().contains("file://")) { + throw new InvalidParameterValueException("File:// type urls are currently unsupported"); + } + + ImageFormat imgfmt = ImageFormat.valueOf(format.toUpperCase()); + if (imgfmt == null) { + throw new IllegalArgumentException("Image format is incorrect " + format + ". Supported formats are " + EnumUtils.listValues(ImageFormat.values())); + } + String userSpecifiedName = volumeName; if (userSpecifiedName == null) { userSpecifiedName = getRandomVolumeName(); } - if((!url.toLowerCase().endsWith("vhd"))&&(!url.toLowerCase().endsWith("vhd.zip")) - &&(!url.toLowerCase().endsWith("vhd.bz2"))&&(!url.toLowerCase().endsWith("vhd.gz")) - &&(!url.toLowerCase().endsWith("qcow2"))&&(!url.toLowerCase().endsWith("qcow2.zip")) - &&(!url.toLowerCase().endsWith("qcow2.bz2"))&&(!url.toLowerCase().endsWith("qcow2.gz")) - &&(!url.toLowerCase().endsWith("ova"))&&(!url.toLowerCase().endsWith("ova.zip")) - &&(!url.toLowerCase().endsWith("ova.bz2"))&&(!url.toLowerCase().endsWith("ova.gz")) - &&(!url.toLowerCase().endsWith("img"))&&(!url.toLowerCase().endsWith("raw"))){ - throw new InvalidParameterValueException("Please specify a valid " + format.toLowerCase()); - } - - if ((format.equalsIgnoreCase("vhd") && (!url.toLowerCase().endsWith(".vhd") && !url.toLowerCase().endsWith("vhd.zip") && !url.toLowerCase().endsWith("vhd.bz2") && !url.toLowerCase().endsWith("vhd.gz") )) - || (format.equalsIgnoreCase("qcow2") && (!url.toLowerCase().endsWith(".qcow2") && !url.toLowerCase().endsWith("qcow2.zip") && !url.toLowerCase().endsWith("qcow2.bz2") && !url.toLowerCase().endsWith("qcow2.gz") )) - || (format.equalsIgnoreCase("ova") && (!url.toLowerCase().endsWith(".ova") && !url.toLowerCase().endsWith("ova.zip") && !url.toLowerCase().endsWith("ova.bz2") && !url.toLowerCase().endsWith("ova.gz"))) - || (format.equalsIgnoreCase("raw") && (!url.toLowerCase().endsWith(".img") && !url.toLowerCase().endsWith("raw")))) { - throw new InvalidParameterValueException("Please specify a valid URL. URL:" + url + " is an invalid for the format " + format.toLowerCase()); - } - validateUrl(url); - - return false; - } - - private String validateUrl(String url){ - try { - URI uri = new URI(url); - if ((uri.getScheme() == null) || (!uri.getScheme().equalsIgnoreCase("http") - && !uri.getScheme().equalsIgnoreCase("https") && !uri.getScheme().equalsIgnoreCase("file"))) { - throw new IllegalArgumentException("Unsupported scheme for url: " + url); - } + if((!url.toLowerCase().endsWith("vhd"))&&(!url.toLowerCase().endsWith("vhd.zip")) + &&(!url.toLowerCase().endsWith("vhd.bz2"))&&(!url.toLowerCase().endsWith("vhd.gz")) + &&(!url.toLowerCase().endsWith("qcow2"))&&(!url.toLowerCase().endsWith("qcow2.zip")) + &&(!url.toLowerCase().endsWith("qcow2.bz2"))&&(!url.toLowerCase().endsWith("qcow2.gz")) + &&(!url.toLowerCase().endsWith("ova"))&&(!url.toLowerCase().endsWith("ova.zip")) + &&(!url.toLowerCase().endsWith("ova.bz2"))&&(!url.toLowerCase().endsWith("ova.gz")) + &&(!url.toLowerCase().endsWith("img"))&&(!url.toLowerCase().endsWith("raw"))){ + throw new InvalidParameterValueException("Please specify a valid " + format.toLowerCase()); + } - int port = uri.getPort(); - if (!(port == 80 || port == 443 || port == -1)) { - throw new IllegalArgumentException("Only ports 80 and 443 are allowed"); - } - String host = uri.getHost(); - try { - InetAddress hostAddr = InetAddress.getByName(host); - if (hostAddr.isAnyLocalAddress() || hostAddr.isLinkLocalAddress() || hostAddr.isLoopbackAddress() || hostAddr.isMulticastAddress()) { - throw new IllegalArgumentException("Illegal host specified in url"); - } - if (hostAddr instanceof Inet6Address) { - throw new IllegalArgumentException("IPV6 addresses not supported (" + hostAddr.getHostAddress() + ")"); - } - } catch (UnknownHostException uhe) { - throw new IllegalArgumentException("Unable to resolve " + host); - } - - return uri.toString(); - } catch (URISyntaxException e) { - throw new IllegalArgumentException("Invalid URL " + url); - } - + if ((format.equalsIgnoreCase("vhd") && (!url.toLowerCase().endsWith(".vhd") && !url.toLowerCase().endsWith("vhd.zip") && !url.toLowerCase().endsWith("vhd.bz2") && !url.toLowerCase().endsWith("vhd.gz") )) + || (format.equalsIgnoreCase("qcow2") && (!url.toLowerCase().endsWith(".qcow2") && !url.toLowerCase().endsWith("qcow2.zip") && !url.toLowerCase().endsWith("qcow2.bz2") && !url.toLowerCase().endsWith("qcow2.gz") )) + || (format.equalsIgnoreCase("ova") && (!url.toLowerCase().endsWith(".ova") && !url.toLowerCase().endsWith("ova.zip") && !url.toLowerCase().endsWith("ova.bz2") && !url.toLowerCase().endsWith("ova.gz"))) + || (format.equalsIgnoreCase("raw") && (!url.toLowerCase().endsWith(".img") && !url.toLowerCase().endsWith("raw")))) { + throw new InvalidParameterValueException("Please specify a valid URL. URL:" + url + " is an invalid for the format " + format.toLowerCase()); + } + validateUrl(url); + + return false; } - + + private String validateUrl(String url){ + try { + URI uri = new URI(url); + if ((uri.getScheme() == null) || (!uri.getScheme().equalsIgnoreCase("http") + && !uri.getScheme().equalsIgnoreCase("https") && !uri.getScheme().equalsIgnoreCase("file"))) { + throw new IllegalArgumentException("Unsupported scheme for url: " + url); + } + + int port = uri.getPort(); + if (!(port == 80 || port == 443 || port == -1)) { + throw new IllegalArgumentException("Only ports 80 and 443 are allowed"); + } + String host = uri.getHost(); + try { + InetAddress hostAddr = InetAddress.getByName(host); + if (hostAddr.isAnyLocalAddress() || hostAddr.isLinkLocalAddress() || hostAddr.isLoopbackAddress() || hostAddr.isMulticastAddress()) { + throw new IllegalArgumentException("Illegal host specified in url"); + } + if (hostAddr instanceof Inet6Address) { + throw new IllegalArgumentException("IPV6 addresses not supported (" + hostAddr.getHostAddress() + ")"); + } + } catch (UnknownHostException uhe) { + throw new IllegalArgumentException("Unable to resolve " + host); + } + + return uri.toString(); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Invalid URL " + url); + } + + } + private VolumeVO persistVolume(Account caller, long ownerId, Long zoneId, String volumeName, String url, String format) { - + Transaction txn = Transaction.currentTxn(); txn.start(); @@ -1861,21 +1858,21 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag volume = _volsDao.persist(volume); try { - stateTransitTo(volume, Event.UploadRequested); - } catch (NoTransitionException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + stateTransitTo(volume, Event.UploadRequested); + } catch (NoTransitionException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } UserContext.current().setEventDetails("Volume Id: " + volume.getId()); // Increment resource count during allocation; if actual creation fails, decrement it _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.volume); txn.commit(); - return volume; - } - - + return volume; + } + + /* * Just allocate a volume in the database, don't send the createvolume cmd to hypervisor. The volume will be finally * created @@ -2049,9 +2046,9 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag volume = _volsDao.persist(volume); if(cmd.getSnapshotId() == null){ - //for volume created from snapshot, create usage event after volume creation - UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), diskOfferingId, null, size); - _usageEventDao.persist(usageEvent); + //for volume created from snapshot, create usage event after volume creation + UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), diskOfferingId, null, size); + _usageEventDao.persist(usageEvent); } UserContext.current().setEventDetails("Volume Id: " + volume.getId()); @@ -2161,7 +2158,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag if (capacities.size() == 0) { CapacityVO capacity = new CapacityVO(storagePool.getId(), storagePool.getDataCenterId(), storagePool.getPodId(), storagePool.getClusterId(), allocated, totalOverProvCapacity, capacityType); CapacityState capacityState = _configMgr.findClusterAllocationState(ApiDBUtils.findClusterById(storagePool.getClusterId())) == AllocationState.Disabled ? - CapacityState.Disabled : CapacityState.Enabled; + CapacityState.Disabled : CapacityState.Enabled; capacity.setCapacityState(capacityState); _capacityDao.persist(capacity); } else { @@ -2182,7 +2179,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag s_logger.debug("Successfully set Capacity - " + totalOverProvCapacity + " for capacity type - " + capacityType + " , DataCenterId - " + storagePool.getDataCenterId() + ", HostOrPoolId - " + storagePool.getId() + ", PodId " + storagePool.getPodId()); } - + @Override public List getUpHostsInPool(long poolId) { SearchCriteria sc = UpHostsInPoolSearch.create(); @@ -2283,7 +2280,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag s_logger.warn("Unable to destroy " + vol.getId(), e); } } - + // remove snapshots in Error state List snapshots = _snapshotDao.listAllByStatus(Snapshot.Status.Error); for (SnapshotVO snapshotVO : snapshots) { @@ -2293,7 +2290,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag s_logger.warn("Unable to destroy " + snapshotVO.getId(), e); } } - + } finally { scanLock.unlock(); } @@ -2432,7 +2429,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag s_logger.warn("problem cleaning up snapshots in secondary storage " + secondaryStorageHost, e2); } } - + //CleanUp volumes on Secondary Storage. for (HostVO secondaryStorageHost : secondaryStorageHosts) { try { @@ -2460,7 +2457,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag _volumeHostDao.remove(destroyedVolumeHostVO.getId()); } } - + }catch (Exception e2) { s_logger.warn("problem cleaning up volumes in secondary storage " + secondaryStorageHost, e2); } @@ -2894,12 +2891,12 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag // Check that volume is completely Uploaded if (volume.getState() == Volume.State.UploadOp){ - VolumeHostVO volumeHost = _volumeHostDao.findByVolumeId(volume.getId()); + VolumeHostVO volumeHost = _volumeHostDao.findByVolumeId(volume.getId()); if (volumeHost.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS){ - throw new InvalidParameterValueException("Please specify a volume that is not uploading"); + throw new InvalidParameterValueException("Please specify a volume that is not uploading"); } } - + // Check that the volume is not already destroyed if (volume.getState() != Volume.State.Destroy) { if (!destroyVolume(volume)) { @@ -3109,7 +3106,6 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag volIds.add(volume.getId()); } - checkPointTaskId = _checkPointMgr.pushCheckPoint(new StorageMigrationCleanupMaid(StorageMigrationCleanupMaid.StorageMigrationState.MIGRATING, volIds)); transitResult = true; } finally { if (!transitResult) { @@ -3166,7 +3162,6 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag s_logger.debug("Failed to change volume state: " + e.toString()); } } - _checkPointMgr.popCheckPoint(checkPointTaskId); } else { // Need a transaction, make sure all the volumes get migrated to new storage pool txn = Transaction.currentTxn(); @@ -3192,11 +3187,6 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag } } transitResult = true; - try { - _checkPointMgr.popCheckPoint(checkPointTaskId); - } catch (Exception e) { - - } } finally { if (!transitResult) { txn.rollback(); @@ -3259,7 +3249,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag if (s_logger.isDebugEnabled()) { s_logger.debug("Checking if we need to prepare " + vols.size() + " volumes for " + vm); } - + boolean recreate = _recreateSystemVmEnabled; List recreateVols = new ArrayList(vols.size()); @@ -3270,8 +3260,8 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag assignedPool = dest.getStorageForDisks().get(vol); } if (assignedPool == null && recreate) { - assignedPool = _storagePoolDao.findById(vol.getPoolId()); - + assignedPool = _storagePoolDao.findById(vol.getPoolId()); + } if (assignedPool != null || recreate) { Volume.State state = vol.getState(); @@ -3312,7 +3302,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag StoragePoolVO pool = _storagePoolDao.findById(vol.getPoolId()); vm.addDisk(new VolumeTO(vol, pool)); } - + } } } else { @@ -3331,10 +3321,10 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag VolumeVO newVol; StoragePool existingPool = null; if (recreate && (dest.getStorageForDisks() == null || dest.getStorageForDisks().get(vol) == null)) { - existingPool = _storagePoolDao.findById(vol.getPoolId()); - s_logger.debug("existing pool: " + existingPool.getId()); + existingPool = _storagePoolDao.findById(vol.getPoolId()); + s_logger.debug("existing pool: " + existingPool.getId()); } - + if (vol.getState() == Volume.State.Allocated || vol.getState() == Volume.State.Creating) { newVol = vol; } else { @@ -3429,12 +3419,12 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag if (toBeCreated.getTemplateId() != null) { template = _templateDao.findById(toBeCreated.getTemplateId()); } - + StoragePool pool = null; if (sPool != null) { - pool = sPool; + pool = sPool; } else { - pool = dest.getStorageForDisks().get(toBeCreated); + pool = dest.getStorageForDisks().get(toBeCreated); } if (pool != null) { @@ -3465,12 +3455,12 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag String fullTmpltUrl = tmpltHostUrl + "/" + tmpltHostOn.getInstallPath(); cmd = new CreateCommand(diskProfile, fullTmpltUrl, new StorageFilerTO(pool)); } else { - tmpltStoredOn = _tmpltMgr.prepareTemplateForCreate(template, pool); - if (tmpltStoredOn == null) { - s_logger.debug("Cannot use this pool " + pool + " because we can't propagate template " + template); - return null; - } - cmd = new CreateCommand(diskProfile, tmpltStoredOn.getLocalDownloadPath(), new StorageFilerTO(pool)); + tmpltStoredOn = _tmpltMgr.prepareTemplateForCreate(template, pool); + if (tmpltStoredOn == null) { + s_logger.debug("Cannot use this pool " + pool + " because we can't propagate template " + template); + return null; + } + cmd = new CreateCommand(diskProfile, tmpltStoredOn.getLocalDownloadPath(), new StorageFilerTO(pool)); } } else { if (template != null && Storage.ImageFormat.ISO == template.getFormat()) { @@ -3513,27 +3503,27 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag if (s_logger.isDebugEnabled()) { s_logger.debug("Expunging " + vol); } - + //Find out if the volume is present on secondary storage VolumeHostVO volumeHost = _volumeHostDao.findByVolumeId(vol.getId()); if(volumeHost != null){ - if (volumeHost.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED){ - HostVO ssHost = _hostDao.findById(volumeHost.getHostId()); - DeleteVolumeCommand dtCommand = new DeleteVolumeCommand(ssHost.getStorageUrl(), volumeHost.getInstallPath()); - Answer answer = _agentMgr.sendToSecStorage(ssHost, dtCommand); - if (answer == null || !answer.getResult()) { - s_logger.debug("Failed to delete " + volumeHost + " due to " + ((answer == null) ? "answer is null" : answer.getDetails())); - return; - } - }else if(volumeHost.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS){ - s_logger.debug("Volume: " + vol.getName() + " is currently being uploaded; cant' delete it."); - throw new CloudRuntimeException("Please specify a volume that is not currently being uploaded."); - } + if (volumeHost.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED){ + HostVO ssHost = _hostDao.findById(volumeHost.getHostId()); + DeleteVolumeCommand dtCommand = new DeleteVolumeCommand(ssHost.getStorageUrl(), volumeHost.getInstallPath()); + Answer answer = _agentMgr.sendToSecStorage(ssHost, dtCommand); + if (answer == null || !answer.getResult()) { + s_logger.debug("Failed to delete " + volumeHost + " due to " + ((answer == null) ? "answer is null" : answer.getDetails())); + return; + } + }else if(volumeHost.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS){ + s_logger.debug("Volume: " + vol.getName() + " is currently being uploaded; cant' delete it."); + throw new CloudRuntimeException("Please specify a volume that is not currently being uploaded."); + } _volumeHostDao.remove(volumeHost.getId()); _volumeDao.remove(vol.getId()); return; } - + String vmName = null; if (vol.getVolumeType() == Type.ROOT && vol.getInstanceId() != null) { VirtualMachine vm = _vmInstanceDao.findByIdIncludingRemoved(vol.getInstanceId()); @@ -3578,7 +3568,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag } catch (RuntimeException ex) { if (force) { s_logger.info("Failed to expunge volume, but marking volume id=" + vol.getId() + " as expunged anyway " + - "due to force=true. Volume failed to expunge due to ", ex); + "due to force=true. Volume failed to expunge due to ", ex); removeVolume = true; } else { throw ex; @@ -3869,14 +3859,14 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag return null; } } - + @Override public HypervisorType getHypervisorTypeFromFormat(ImageFormat format) { - - if(format == null) { + + if(format == null) { return HypervisorType.None; - } - + } + if (format == ImageFormat.VHD) { return HypervisorType.XenServer; } else if (format == ImageFormat.OVA) { @@ -3965,5 +3955,5 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag } return true; } - + } diff --git a/server/src/com/cloud/storage/StorageMigrationCleanupMaid.java b/server/src/com/cloud/storage/StorageMigrationCleanupMaid.java deleted file mode 100644 index 03a49760f98..00000000000 --- a/server/src/com/cloud/storage/StorageMigrationCleanupMaid.java +++ /dev/null @@ -1,121 +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. -package com.cloud.storage; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.log4j.Logger; - -import com.cloud.cluster.CheckPointManager; -import com.cloud.cluster.CleanupMaid; -import com.cloud.server.ManagementServer; -import com.cloud.storage.dao.VolumeDao; - -import com.cloud.utils.db.Transaction; -import com.cloud.utils.fsm.NoTransitionException; -import com.cloud.utils.fsm.StateMachine2; -import com.cloud.vm.VMInstanceVO; -import com.cloud.vm.VirtualMachine; -import com.cloud.vm.VirtualMachineManager; -import com.cloud.vm.dao.VMInstanceDao; - -public class StorageMigrationCleanupMaid implements CleanupMaid { - private static final Logger s_logger = Logger.getLogger(StorageMigrationCleanupMaid.class); - public static enum StorageMigrationState { - MIGRATING, - MIGRATINGFAILED, - MIGRATINGSUCCESS; - } - - private List _volumesIds = new ArrayList(); - private StorageMigrationState _migrateState; - - public StorageMigrationCleanupMaid() { - - } - - public StorageMigrationCleanupMaid(StorageMigrationState state, List volumes) { - _migrateState = state; - _volumesIds = volumes; - } - - public void updateStaste(StorageMigrationState state) { - _migrateState = state; - } - - @Override - public int cleanup(CheckPointManager checkPointMgr) { - StateMachine2 _stateMachine = Volume.State.getStateMachine(); - - ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); - VolumeDao volDao = locator.getDao(VolumeDao.class); - VMInstanceDao vmDao = locator.getDao(VMInstanceDao.class); - VirtualMachineManager vmMgr = locator.getManager(VirtualMachineManager.class); - Long vmInstanceId = null; - boolean success = true; - Transaction txn = Transaction.open(Transaction.CLOUD_DB); - - try { - txn.start(); - for (Long volumeId : _volumesIds) { - VolumeVO volume = volDao.findById(volumeId); - if (volume == null) { - continue; - } - vmInstanceId = volume.getInstanceId(); - if (_migrateState == StorageMigrationState.MIGRATING && volume.getState() == Volume.State.Migrating) { - try { - _stateMachine.transitTo(volume, Volume.Event.OperationFailed, null, volDao); - } catch (NoTransitionException e) { - s_logger.debug("Failed to transit volume state: " + e.toString()); - success = false; - break; - } - } - } - if (vmInstanceId != null) { - VMInstanceVO vm = vmDao.findById(vmInstanceId); - if (vm != null && vm.getState() == VirtualMachine.State.Migrating) { - try { - vmMgr.stateTransitTo(vm, VirtualMachine.Event.AgentReportStopped, null); - } catch (NoTransitionException e) { - s_logger.debug("Failed to transit vm state"); - success = false; - } - } - } - - if (success) { - txn.commit(); - } - } catch (Exception e) { - s_logger.debug("storage migration cleanup failed:" + e.toString()); - txn.rollback(); - }finally { - txn.close(); - } - - return 0; - } - - @Override - public String getCleanupProcedure() { - return null; - } - -} diff --git a/server/src/com/cloud/storage/allocator/GarbageCollectingStoragePoolAllocator.java b/server/src/com/cloud/storage/allocator/GarbageCollectingStoragePoolAllocator.java index dbef8d5f250..4eeae280d8b 100644 --- a/server/src/com/cloud/storage/allocator/GarbageCollectingStoragePoolAllocator.java +++ b/server/src/com/cloud/storage/allocator/GarbageCollectingStoragePoolAllocator.java @@ -31,7 +31,7 @@ import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; - +import com.cloud.utils.component.ComponentContext; import com.cloud.vm.DiskProfile; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; @@ -40,68 +40,66 @@ import com.cloud.vm.VirtualMachineProfile; @Local(value=StoragePoolAllocator.class) public class GarbageCollectingStoragePoolAllocator extends AbstractStoragePoolAllocator { private static final Logger s_logger = Logger.getLogger(GarbageCollectingStoragePoolAllocator.class); - + StoragePoolAllocator _firstFitStoragePoolAllocator; StoragePoolAllocator _localStoragePoolAllocator; @Inject StorageManager _storageMgr; @Inject ConfigurationDao _configDao; boolean _storagePoolCleanupEnabled; - + @Override public boolean allocatorIsCorrectType(DiskProfile dskCh) { - return true; + return true; } - + public Integer getStorageOverprovisioningFactor() { - return null; + return null; } - + public Long getExtraBytesPerVolume() { - return null; + return null; } - + @Override public List allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo) { - - if (!_storagePoolCleanupEnabled) { - s_logger.debug("Storage pool cleanup is not enabled, so GarbageCollectingStoragePoolAllocator is being skipped."); - return null; - } - - // Clean up all storage pools - _storageMgr.cleanupStorage(false); - // Determine what allocator to use - StoragePoolAllocator allocator; - if (localStorageAllocationNeeded(dskCh)) { - allocator = _localStoragePoolAllocator; - } else { - allocator = _firstFitStoragePoolAllocator; - } - // Try to find a storage pool after cleanup + if (!_storagePoolCleanupEnabled) { + s_logger.debug("Storage pool cleanup is not enabled, so GarbageCollectingStoragePoolAllocator is being skipped."); + return null; + } + + // Clean up all storage pools + _storageMgr.cleanupStorage(false); + // Determine what allocator to use + StoragePoolAllocator allocator; + if (localStorageAllocationNeeded(dskCh)) { + allocator = _localStoragePoolAllocator; + } else { + allocator = _firstFitStoragePoolAllocator; + } + + // Try to find a storage pool after cleanup ExcludeList myAvoids = new ExcludeList(avoid.getDataCentersToAvoid(), avoid.getPodsToAvoid(), avoid.getClustersToAvoid(), avoid.getHostsToAvoid(), avoid.getPoolsToAvoid()); - + return allocator.allocateToPool(dskCh, vmProfile, plan, myAvoids, returnUpTo); } @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); - - ComponentLocator locator = ComponentLocator.getCurrentLocator(); - - _firstFitStoragePoolAllocator = ComponentLocator.inject(FirstFitStoragePoolAllocator.class); + + _firstFitStoragePoolAllocator = ComponentContext.inject(FirstFitStoragePoolAllocator.class); _firstFitStoragePoolAllocator.configure("GCFirstFitStoragePoolAllocator", params); - _localStoragePoolAllocator = ComponentLocator.inject(LocalStoragePoolAllocator.class); + _localStoragePoolAllocator = ComponentContext.inject(LocalStoragePoolAllocator.class); _localStoragePoolAllocator.configure("GCLocalStoragePoolAllocator", params); - + String storagePoolCleanupEnabled = _configDao.getValue("storage.pool.cleanup.enabled"); _storagePoolCleanupEnabled = (storagePoolCleanupEnabled == null) ? true : Boolean.parseBoolean(storagePoolCleanupEnabled); - + return true; } - + public GarbageCollectingStoragePoolAllocator() { } - + } diff --git a/server/src/com/cloud/storage/listener/StoragePoolMonitor.java b/server/src/com/cloud/storage/listener/StoragePoolMonitor.java index 3a7dac7a70f..e848a8727a0 100755 --- a/server/src/com/cloud/storage/listener/StoragePoolMonitor.java +++ b/server/src/com/cloud/storage/listener/StoragePoolMonitor.java @@ -18,6 +18,8 @@ package com.cloud.storage.listener; import java.util.List; +import javax.inject.Inject; + import org.apache.log4j.Logger; import com.cloud.agent.Listener; @@ -31,96 +33,93 @@ import com.cloud.exception.ConnectionException; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.server.ManagementService; import com.cloud.storage.OCFS2Manager; +import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageManagerImpl; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StoragePoolVO; -import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.dao.StoragePoolDao; public class StoragePoolMonitor implements Listener { private static final Logger s_logger = Logger.getLogger(StoragePoolMonitor.class); - private final StorageManagerImpl _storageManager; - private final StoragePoolDao _poolDao; - OCFS2Manager _ocfs2Mgr; - + private final StorageManagerImpl _storageManager; + private final StoragePoolDao _poolDao; + @Inject OCFS2Manager _ocfs2Mgr; + public StoragePoolMonitor(StorageManagerImpl mgr, StoragePoolDao poolDao) { - this._storageManager = mgr; - this._poolDao = poolDao; - - ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); - this._ocfs2Mgr = locator.getManager(OCFS2Manager.class); + this._storageManager = mgr; + this._poolDao = poolDao; + } - - + + @Override public boolean isRecurring() { return false; } - + @Override public synchronized boolean processAnswers(long agentId, long seq, Answer[] resp) { return true; } - + @Override public synchronized boolean processDisconnect(long agentId, Status state) { return true; } - + @Override public void processConnect(HostVO host, StartupCommand cmd, boolean forRebalance) throws ConnectionException { - if (cmd instanceof StartupRoutingCommand) { - StartupRoutingCommand scCmd = (StartupRoutingCommand)cmd; - if (scCmd.getHypervisorType() == HypervisorType.XenServer || scCmd.getHypervisorType() == HypervisorType.KVM || - scCmd.getHypervisorType() == HypervisorType.VMware || scCmd.getHypervisorType() == HypervisorType.Simulator || scCmd.getHypervisorType() == HypervisorType.Ovm) { - List pools = _poolDao.listBy(host.getDataCenterId(), host.getPodId(), host.getClusterId()); - for (StoragePoolVO pool : pools) { - if (pool.getStatus() != StoragePoolStatus.Up) { - continue; - } - if (!pool.getPoolType().isShared()) { - continue; - } - - if (pool.getPoolType() == StoragePoolType.OCFS2 && !_ocfs2Mgr.prepareNodes(pool.getClusterId())) { - throw new ConnectionException(true, "Unable to prepare OCFS2 nodes for pool " + pool.getId()); - } - - Long hostId = host.getId(); - s_logger.debug("Host " + hostId + " connected, sending down storage pool information ..."); - try { - _storageManager.connectHostToSharedPool(hostId, pool); - _storageManager.createCapacityEntry(pool); - } catch (Exception e) { - s_logger.warn("Unable to connect host " + hostId + " to pool " + pool + " due to " + e.toString(), e); - } - } - } - } + if (cmd instanceof StartupRoutingCommand) { + StartupRoutingCommand scCmd = (StartupRoutingCommand)cmd; + if (scCmd.getHypervisorType() == HypervisorType.XenServer || scCmd.getHypervisorType() == HypervisorType.KVM || + scCmd.getHypervisorType() == HypervisorType.VMware || scCmd.getHypervisorType() == HypervisorType.Simulator || scCmd.getHypervisorType() == HypervisorType.Ovm) { + List pools = _poolDao.listBy(host.getDataCenterId(), host.getPodId(), host.getClusterId()); + for (StoragePoolVO pool : pools) { + if (pool.getStatus() != StoragePoolStatus.Up) { + continue; + } + if (!pool.getPoolType().isShared()) { + continue; + } + + if (pool.getPoolType() == StoragePoolType.OCFS2 && !_ocfs2Mgr.prepareNodes(pool.getClusterId())) { + throw new ConnectionException(true, "Unable to prepare OCFS2 nodes for pool " + pool.getId()); + } + + Long hostId = host.getId(); + s_logger.debug("Host " + hostId + " connected, sending down storage pool information ..."); + try { + _storageManager.connectHostToSharedPool(hostId, pool); + _storageManager.createCapacityEntry(pool); + } catch (Exception e) { + s_logger.warn("Unable to connect host " + hostId + " to pool " + pool + " due to " + e.toString(), e); + } + } + } + } } - + @Override public boolean processCommands(long agentId, long seq, Command[] req) { return false; } - + @Override public AgentControlAnswer processControlCommand(long agentId, AgentControlCommand cmd) { - return null; + return null; } - + @Override public boolean processTimeout(long agentId, long seq) { - return true; + return true; } - + @Override public int getTimeout() { - return -1; + return -1; } - + } diff --git a/server/src/com/cloud/storage/resource/DummySecondaryStorageResource.java b/server/src/com/cloud/storage/resource/DummySecondaryStorageResource.java index 1f33330811b..2773e293239 100644 --- a/server/src/com/cloud/storage/resource/DummySecondaryStorageResource.java +++ b/server/src/com/cloud/storage/resource/DummySecondaryStorageResource.java @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; @@ -54,38 +55,38 @@ import com.cloud.storage.template.TemplateInfo; public class DummySecondaryStorageResource extends ServerResourceBase implements ServerResource { private static final Logger s_logger = Logger.getLogger(DummySecondaryStorageResource.class); - + String _dc; String _pod; String _guid; String _dummyPath; - VMTemplateDao _tmpltDao; - private boolean _useServiceVm; - - - public DummySecondaryStorageResource(boolean useServiceVM) { - setUseServiceVm(useServiceVM); - } + @Inject VMTemplateDao _tmpltDao; + private boolean _useServiceVm; - @Override - protected String getDefaultScriptsDir() { - return "dummy"; - } - @Override - public Answer executeRequest(Command cmd) { + public DummySecondaryStorageResource(boolean useServiceVM) { + setUseServiceVm(useServiceVM); + } + + @Override + protected String getDefaultScriptsDir() { + return "dummy"; + } + + @Override + public Answer executeRequest(Command cmd) { if (cmd instanceof DownloadProgressCommand) { return new DownloadAnswer(null, 100, cmd, - com.cloud.storage.VMTemplateStorageResourceAssoc.Status.DOWNLOADED, - "dummyFS", - "/dummy"); + com.cloud.storage.VMTemplateStorageResourceAssoc.Status.DOWNLOADED, + "dummyFS", + "/dummy"); } else if (cmd instanceof DownloadCommand) { return new DownloadAnswer(null, 100, cmd, - com.cloud.storage.VMTemplateStorageResourceAssoc.Status.DOWNLOADED, - "dummyFS", - "/dummy"); + com.cloud.storage.VMTemplateStorageResourceAssoc.Status.DOWNLOADED, + "dummyFS", + "/dummy"); } else if (cmd instanceof GetStorageStatsCommand) { - return execute((GetStorageStatsCommand)cmd); + return execute((GetStorageStatsCommand)cmd); } else if (cmd instanceof CheckHealthCommand) { return new CheckHealthAnswer((CheckHealthCommand)cmd, true); } else if (cmd instanceof ReadyCommand) { @@ -93,33 +94,33 @@ public class DummySecondaryStorageResource extends ServerResourceBase implements } else { return Answer.createUnsupportedCommandAnswer(cmd); } - } + } - @Override - public PingCommand getCurrentStatus(long id) { + @Override + public PingCommand getCurrentStatus(long id) { return new PingStorageCommand(Host.Type.Storage, id, new HashMap()); - } + } - @Override - public Type getType() { + @Override + public Type getType() { return Host.Type.SecondaryStorage; - } + } - @Override - public StartupCommand[] initialize() { + @Override + public StartupCommand[] initialize() { final StartupStorageCommand cmd = new StartupStorageCommand("dummy", - StoragePoolType.NetworkFilesystem, 1024*1024*1024*100L, - new HashMap()); - + StoragePoolType.NetworkFilesystem, 1024*1024*1024*100L, + new HashMap()); + cmd.setResourceType(Storage.StorageResourceType.SECONDARY_STORAGE); cmd.setIqn(null); cmd.setNfsShare(_guid); - + fillNetworkInformation(cmd); cmd.setDataCenter(_dc); cmd.setPod(_pod); cmd.setGuid(_guid); - + cmd.setName(_guid); cmd.setVersion(DummySecondaryStorageResource.class.getPackage().getImplementationVersion()); /* gather TemplateInfo in second storage */ @@ -127,62 +128,57 @@ public class DummySecondaryStorageResource extends ServerResourceBase implements cmd.getHostDetails().put("mount.parent", "dummy"); cmd.getHostDetails().put("mount.path", "dummy"); cmd.getHostDetails().put("orig.url", _guid); - + String tok[] = _dummyPath.split(":"); cmd.setPrivateIpAddress(tok[0]); return new StartupCommand [] {cmd}; - } - + } + protected GetStorageStatsAnswer execute(GetStorageStatsCommand cmd) { long size = 1024*1024*1024*100L; return new GetStorageStatsAnswer(cmd, 0, size); } - + @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); - + _guid = (String)params.get("guid"); if (_guid == null) { throw new ConfigurationException("Unable to find the guid"); } - + _dc = (String)params.get("zone"); if (_dc == null) { throw new ConfigurationException("Unable to find the zone"); } _pod = (String)params.get("pod"); - + _dummyPath = (String)params.get("mount.path"); if (_dummyPath == null) { throw new ConfigurationException("Unable to find mount.path"); } - - ComponentLocator locator = ComponentLocator.getLocator("management-server"); - _tmpltDao = locator.getDao(VMTemplateDao.class); - if (_tmpltDao == null) { - throw new ConfigurationException("Unable to find VMTemplate dao"); - } + return true; } - public void setUseServiceVm(boolean _useServiceVm) { - this._useServiceVm = _useServiceVm; - } + public void setUseServiceVm(boolean _useServiceVm) { + this._useServiceVm = _useServiceVm; + } - public boolean useServiceVm() { - return _useServiceVm; - } - - public Map getDefaultSystemVmTemplateInfo() { - List tmplts = _tmpltDao.listAllSystemVMTemplates(); - Map tmpltInfo = new HashMap(); - if (tmplts != null) { - for (VMTemplateVO tmplt : tmplts) { - TemplateInfo routingInfo = new TemplateInfo(tmplt.getUniqueName(), TemplateConstants.DEFAULT_SYSTEM_VM_TEMPLATE_PATH + tmplt.getId() + File.separator, false, false); - tmpltInfo.put(tmplt.getUniqueName(), routingInfo); - } - } - return tmpltInfo; - } + public boolean useServiceVm() { + return _useServiceVm; + } + + public Map getDefaultSystemVmTemplateInfo() { + List tmplts = _tmpltDao.listAllSystemVMTemplates(); + Map tmpltInfo = new HashMap(); + if (tmplts != null) { + for (VMTemplateVO tmplt : tmplts) { + TemplateInfo routingInfo = new TemplateInfo(tmplt.getUniqueName(), TemplateConstants.DEFAULT_SYSTEM_VM_TEMPLATE_PATH + tmplt.getId() + File.separator, false, false); + tmpltInfo.put(tmplt.getUniqueName(), routingInfo); + } + } + return tmpltInfo; + } } diff --git a/server/src/com/cloud/test/DatabaseConfig.java b/server/src/com/cloud/test/DatabaseConfig.java index 03cf083b610..f0e9d82ea8d 100755 --- a/server/src/com/cloud/test/DatabaseConfig.java +++ b/server/src/com/cloud/test/DatabaseConfig.java @@ -54,7 +54,7 @@ import com.cloud.service.dao.ServiceOfferingDaoImpl; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.dao.DiskOfferingDaoImpl; import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.component.LegacyComponentLocator; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; import com.cloud.utils.net.NfsUtils; @@ -74,86 +74,86 @@ public class DatabaseConfig { // Change to HashSet private static HashSet objectNames = new HashSet(); private static HashSet fieldNames = new HashSet(); - + // Maintain an IPRangeConfig object to handle IP related logic - private final IPRangeConfig iprc = LegacyComponentLocator.inject(IPRangeConfig.class); - + private final IPRangeConfig iprc = ComponentContext.inject(IPRangeConfig.class); + // Maintain a PodZoneConfig object to handle Pod/Zone related logic - private final PodZoneConfig pzc = LegacyComponentLocator.inject(PodZoneConfig.class); - + private final PodZoneConfig pzc = ComponentContext.inject(PodZoneConfig.class); + // Global variables to store network.throttling.rate and multicast.throttling.rate from the configuration table // Will be changed from null to a non-null value if the value existed in the configuration table private String _networkThrottlingRate = null; private String _multicastThrottlingRate = null; - + static { - // initialize the objectNames ArrayList - objectNames.add("zone"); + // initialize the objectNames ArrayList + objectNames.add("zone"); objectNames.add("physicalNetwork"); - objectNames.add("vlan"); - objectNames.add("pod"); + objectNames.add("vlan"); + objectNames.add("pod"); objectNames.add("cluster"); - objectNames.add("storagePool"); - objectNames.add("secondaryStorage"); - objectNames.add("serviceOffering"); + objectNames.add("storagePool"); + objectNames.add("secondaryStorage"); + objectNames.add("serviceOffering"); objectNames.add("diskOffering"); - objectNames.add("user"); - objectNames.add("pricing"); - objectNames.add("configuration"); - objectNames.add("privateIpAddresses"); - objectNames.add("publicIpAddresses"); + objectNames.add("user"); + objectNames.add("pricing"); + objectNames.add("configuration"); + objectNames.add("privateIpAddresses"); + objectNames.add("publicIpAddresses"); objectNames.add("physicalNetworkServiceProvider"); objectNames.add("virtualRouterProvider"); - - // initialize the fieldNames ArrayList - fieldNames.add("id"); - fieldNames.add("name"); - fieldNames.add("dns1"); - fieldNames.add("dns2"); - fieldNames.add("internalDns1"); - fieldNames.add("internalDns2"); - fieldNames.add("guestNetworkCidr"); - fieldNames.add("gateway"); - fieldNames.add("netmask"); - fieldNames.add("vncConsoleIp"); - fieldNames.add("zoneId"); - fieldNames.add("vlanId"); - fieldNames.add("cpu"); - fieldNames.add("ramSize"); - fieldNames.add("speed"); - fieldNames.add("useLocalStorage"); - fieldNames.add("hypervisorType"); - fieldNames.add("diskSpace"); - fieldNames.add("nwRate"); - fieldNames.add("mcRate"); - fieldNames.add("price"); - fieldNames.add("username"); - fieldNames.add("password"); - fieldNames.add("firstname"); - fieldNames.add("lastname"); - fieldNames.add("email"); - fieldNames.add("priceUnit"); - fieldNames.add("type"); - fieldNames.add("value"); - fieldNames.add("podId"); - fieldNames.add("podName"); - fieldNames.add("ipAddressRange"); - fieldNames.add("vlanType"); - fieldNames.add("vlanName"); - fieldNames.add("cidr"); - fieldNames.add("vnet"); - fieldNames.add("mirrored"); - fieldNames.add("enableHA"); - fieldNames.add("displayText"); - fieldNames.add("domainId"); - fieldNames.add("hostAddress"); - fieldNames.add("hostPath"); - fieldNames.add("guestIpType"); - fieldNames.add("url"); - fieldNames.add("storageType"); - fieldNames.add("category"); - fieldNames.add("tags"); - fieldNames.add("networktype"); + + // initialize the fieldNames ArrayList + fieldNames.add("id"); + fieldNames.add("name"); + fieldNames.add("dns1"); + fieldNames.add("dns2"); + fieldNames.add("internalDns1"); + fieldNames.add("internalDns2"); + fieldNames.add("guestNetworkCidr"); + fieldNames.add("gateway"); + fieldNames.add("netmask"); + fieldNames.add("vncConsoleIp"); + fieldNames.add("zoneId"); + fieldNames.add("vlanId"); + fieldNames.add("cpu"); + fieldNames.add("ramSize"); + fieldNames.add("speed"); + fieldNames.add("useLocalStorage"); + fieldNames.add("hypervisorType"); + fieldNames.add("diskSpace"); + fieldNames.add("nwRate"); + fieldNames.add("mcRate"); + fieldNames.add("price"); + fieldNames.add("username"); + fieldNames.add("password"); + fieldNames.add("firstname"); + fieldNames.add("lastname"); + fieldNames.add("email"); + fieldNames.add("priceUnit"); + fieldNames.add("type"); + fieldNames.add("value"); + fieldNames.add("podId"); + fieldNames.add("podName"); + fieldNames.add("ipAddressRange"); + fieldNames.add("vlanType"); + fieldNames.add("vlanName"); + fieldNames.add("cidr"); + fieldNames.add("vnet"); + fieldNames.add("mirrored"); + fieldNames.add("enableHA"); + fieldNames.add("displayText"); + fieldNames.add("domainId"); + fieldNames.add("hostAddress"); + fieldNames.add("hostPath"); + fieldNames.add("guestIpType"); + fieldNames.add("url"); + fieldNames.add("storageType"); + fieldNames.add("category"); + fieldNames.add("tags"); + fieldNames.add("networktype"); fieldNames.add("clusterId"); fieldNames.add("physicalNetworkId"); fieldNames.add("destPhysicalNetworkId"); @@ -169,7 +169,7 @@ public class DatabaseConfig { fieldNames.add("userData"); fieldNames.add("securityGroup"); fieldNames.add("nspId"); - + s_configurationDescriptions.put("host.stats.interval", "the interval in milliseconds when host stats are retrieved from agents"); s_configurationDescriptions.put("storage.stats.interval", "the interval in milliseconds when storage stats (per host) are retrieved from agents"); s_configurationDescriptions.put("volume.stats.interval", "the interval in milliseconds when volume stats are retrieved from agents"); @@ -220,17 +220,17 @@ public class DatabaseConfig { s_configurationDescriptions.put("snapshot.test.weeks.per.month", "Set it to a smaller value to take more recurring snapshots"); s_configurationDescriptions.put("snapshot.test.months.per.year", "Set it to a smaller value to take more recurring snapshots"); s_configurationDescriptions.put("hypervisor.type", "The type of hypervisor that this deployment will use."); - - + + s_configurationComponents.put("host.stats.interval", "management-server"); s_configurationComponents.put("storage.stats.interval", "management-server"); s_configurationComponents.put("volume.stats.interval", "management-server"); s_configurationComponents.put("integration.api.port", "management-server"); s_configurationComponents.put("usage.stats.job.exec.time", "management-server"); s_configurationComponents.put("usage.stats.job.aggregation.range", "management-server"); - s_configurationComponents.put("consoleproxy.domP.enable", "management-server"); - s_configurationComponents.put("consoleproxy.port", "management-server"); - s_configurationComponents.put("consoleproxy.url.port", "management-server"); + s_configurationComponents.put("consoleproxy.domP.enable", "management-server"); + s_configurationComponents.put("consoleproxy.port", "management-server"); + s_configurationComponents.put("consoleproxy.url.port", "management-server"); s_configurationComponents.put("alert.email.addresses", "management-server"); s_configurationComponents.put("alert.smtp.host", "management-server"); s_configurationComponents.put("alert.smtp.port", "management-server"); @@ -256,22 +256,22 @@ public class DatabaseConfig { s_configurationComponents.put("instance.name", "AgentManager"); s_configurationComponents.put("storage.overprovisioning.factor", "StorageAllocator"); s_configurationComponents.put("retries.per.host", "AgentManager"); - s_configurationComponents.put("start.retry", "AgentManager"); - s_configurationComponents.put("wait", "AgentManager"); - s_configurationComponents.put("ping.timeout", "AgentManager"); - s_configurationComponents.put("ping.interval", "AgentManager"); - s_configurationComponents.put("alert.wait", "AgentManager"); - s_configurationComponents.put("update.wait", "AgentManager"); - s_configurationComponents.put("guest.domain.suffix", "AgentManager"); - s_configurationComponents.put("consoleproxy.ram.size", "AgentManager"); - s_configurationComponents.put("consoleproxy.cmd.port", "AgentManager"); - s_configurationComponents.put("consoleproxy.loadscan.interval", "AgentManager"); - s_configurationComponents.put("consoleproxy.capacityscan.interval", "AgentManager"); - s_configurationComponents.put("consoleproxy.capacity.standby", "AgentManager"); - s_configurationComponents.put("consoleproxy.session.max", "AgentManager"); - s_configurationComponents.put("consoleproxy.session.timeout", "AgentManager"); - s_configurationComponents.put("expunge.workers", "UserVmManager"); - s_configurationComponents.put("extract.url.cleanup.interval", "management-server"); + s_configurationComponents.put("start.retry", "AgentManager"); + s_configurationComponents.put("wait", "AgentManager"); + s_configurationComponents.put("ping.timeout", "AgentManager"); + s_configurationComponents.put("ping.interval", "AgentManager"); + s_configurationComponents.put("alert.wait", "AgentManager"); + s_configurationComponents.put("update.wait", "AgentManager"); + s_configurationComponents.put("guest.domain.suffix", "AgentManager"); + s_configurationComponents.put("consoleproxy.ram.size", "AgentManager"); + s_configurationComponents.put("consoleproxy.cmd.port", "AgentManager"); + s_configurationComponents.put("consoleproxy.loadscan.interval", "AgentManager"); + s_configurationComponents.put("consoleproxy.capacityscan.interval", "AgentManager"); + s_configurationComponents.put("consoleproxy.capacity.standby", "AgentManager"); + s_configurationComponents.put("consoleproxy.session.max", "AgentManager"); + s_configurationComponents.put("consoleproxy.session.timeout", "AgentManager"); + s_configurationComponents.put("expunge.workers", "UserVmManager"); + s_configurationComponents.put("extract.url.cleanup.interval", "management-server"); s_configurationComponents.put("stop.retry.interval", "HighAvailabilityManager"); s_configurationComponents.put("restart.retry.interval", "HighAvailabilityManager"); s_configurationComponents.put("investigate.retry.interval", "HighAvailabilityManager"); @@ -294,7 +294,7 @@ public class DatabaseConfig { s_configurationComponents.put("snapshot.test.months.per.year", "SnapshotManager"); s_configurationComponents.put("hypervisor.type", "ManagementServer"); - + s_defaultConfigurationValues.put("host.stats.interval", "60000"); s_defaultConfigurationValues.put("storage.stats.interval", "60000"); //s_defaultConfigurationValues.put("volume.stats.interval", "-1"); @@ -336,7 +336,7 @@ public class DatabaseConfig { s_defaultConfigurationValues.put("cpu.overprovisioning.factor", "1"); s_defaultConfigurationValues.put("mem.overprovisioning.factor", "1"); } - + protected DatabaseConfig() { } @@ -346,20 +346,20 @@ public class DatabaseConfig { public static void main(String[] args) { System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"); System.setProperty("javax.xml.parsers.SAXParserFactory", "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"); - + File file = PropertiesUtil.findConfigFile("log4j-cloud.xml"); if(file != null) { - System.out.println("Log4j configuration from : " + file.getAbsolutePath()); - DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000); - } else { - System.out.println("Configure log4j with default properties"); - } - + System.out.println("Log4j configuration from : " + file.getAbsolutePath()); + DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000); + } else { + System.out.println("Configure log4j with default properties"); + } + if (args.length < 1) { s_logger.error("error starting database config, missing initial data file"); } else { try { - DatabaseConfig config = LegacyComponentLocator.inject(DatabaseConfig.class, args[0]); + DatabaseConfig config = ComponentContext.inject(DatabaseConfig.class, args[0]); config.doVersionCheck(); config.doConfig(); System.exit(0); @@ -374,65 +374,65 @@ public class DatabaseConfig { public DatabaseConfig(String configFileName) { _configFileName = configFileName; } - + private void doVersionCheck() { - try { - String warningMsg = "\nYou are using an outdated format for server-setup.xml. Please switch to the new format.\n"; - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - DocumentBuilder dbuilder = dbf.newDocumentBuilder(); - File configFile = new File(_configFileName); - Document d = dbuilder.parse(configFile); - NodeList nodeList = d.getElementsByTagName("version"); - - if (nodeList.getLength() == 0) { - System.out.println(warningMsg); - return; - } - - Node firstNode = nodeList.item(0); - String version = firstNode.getTextContent(); - - if (!version.equals("2.0")) { - System.out.println(warningMsg); - } - - } catch (ParserConfigurationException parserException) { - parserException.printStackTrace(); - } catch (IOException ioException) { - ioException.printStackTrace(); - } catch (SAXException saxException) { - saxException.printStackTrace(); - } + try { + String warningMsg = "\nYou are using an outdated format for server-setup.xml. Please switch to the new format.\n"; + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder dbuilder = dbf.newDocumentBuilder(); + File configFile = new File(_configFileName); + Document d = dbuilder.parse(configFile); + NodeList nodeList = d.getElementsByTagName("version"); + + if (nodeList.getLength() == 0) { + System.out.println(warningMsg); + return; + } + + Node firstNode = nodeList.item(0); + String version = firstNode.getTextContent(); + + if (!version.equals("2.0")) { + System.out.println(warningMsg); + } + + } catch (ParserConfigurationException parserException) { + parserException.printStackTrace(); + } catch (IOException ioException) { + ioException.printStackTrace(); + } catch (SAXException saxException) { + saxException.printStackTrace(); + } } @DB protected void doConfig() { Transaction txn = Transaction.currentTxn(); try { - + File configFile = new File(_configFileName); - + SAXParserFactory spfactory = SAXParserFactory.newInstance(); SAXParser saxParser = spfactory.newSAXParser(); DbConfigXMLHandler handler = new DbConfigXMLHandler(); handler.setParent(this); - + txn.start(); // Save user configured values for all fields saxParser.parse(configFile, handler); - + // Save default values for configuration fields saveVMTemplate(); saveRootDomain(); saveDefaultConfiguations(); - + txn.commit(); // Check pod CIDRs against each other, and against the guest ip network/netmask pzc.checkAllPodCidrSubnets(); - + } catch (Exception ex) { - System.out.print("ERROR IS"+ex); + System.out.print("ERROR IS"+ex); s_logger.error("error", ex); txn.rollback(); } @@ -448,7 +448,7 @@ public class DatabaseConfig { } else if ("physicalNetwork".equals(_currentObjectName)) { savePhysicalNetwork(); } else if ("vlan".equals(_currentObjectName)) { - saveVlan(); + saveVlan(); } else if ("pod".equals(_currentObjectName)) { savePod(); } else if ("serviceOffering".equals(_currentObjectName)) { @@ -460,9 +460,9 @@ public class DatabaseConfig { } else if ("configuration".equals(_currentObjectName)) { saveConfiguration(); } else if ("storagePool".equals(_currentObjectName)) { - saveStoragePool(); + saveStoragePool(); } else if ("secondaryStorage".equals(_currentObjectName)) { - saveSecondaryStorage(); + saveSecondaryStorage(); } else if ("cluster".equals(_currentObjectName)) { saveCluster(); } else if ("physicalNetworkServiceProvider".equals(_currentObjectName)) { @@ -472,88 +472,88 @@ public class DatabaseConfig { } _currentObjectParams = null; } - + @DB public void saveSecondaryStorage() { - long dataCenterId = Long.parseLong(_currentObjectParams.get("zoneId")); - String url = _currentObjectParams.get("url"); - String mountPoint; - try { - mountPoint = NfsUtils.url2Mount(url); - } catch (URISyntaxException e1) { - return; - } - String insertSql1 = "INSERT INTO `host` (`id`, `name`, `status` , `type` , `private_ip_address`, `private_netmask` ,`private_mac_address` , `storage_ip_address` ,`storage_netmask`, `storage_mac_address`, `data_center_id`, `version`, `dom0_memory`, `last_ping`, `resource`, `guid`, `hypervisor_type`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; - String insertSqlHostDetails = "INSERT INTO `host_details` (`id`, `host_id`, `name`, `value`) VALUES(?,?,?,?)"; + long dataCenterId = Long.parseLong(_currentObjectParams.get("zoneId")); + String url = _currentObjectParams.get("url"); + String mountPoint; + try { + mountPoint = NfsUtils.url2Mount(url); + } catch (URISyntaxException e1) { + return; + } + String insertSql1 = "INSERT INTO `host` (`id`, `name`, `status` , `type` , `private_ip_address`, `private_netmask` ,`private_mac_address` , `storage_ip_address` ,`storage_netmask`, `storage_mac_address`, `data_center_id`, `version`, `dom0_memory`, `last_ping`, `resource`, `guid`, `hypervisor_type`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + String insertSqlHostDetails = "INSERT INTO `host_details` (`id`, `host_id`, `name`, `value`) VALUES(?,?,?,?)"; String insertSql2 = "INSERT INTO `op_host` (`id`, `sequence`) VALUES(?, ?)"; - Transaction txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql1); - stmt.setLong(1, 0); - stmt.setString(2, url); - stmt.setString(3, "UP"); - stmt.setString(4, "SecondaryStorage"); - stmt.setString(5, "192.168.122.1"); - stmt.setString(6, "255.255.255.0"); - stmt.setString(7, "92:ff:f5:ad:23:e1"); - stmt.setString(8, "192.168.122.1"); - stmt.setString(9, "255.255.255.0"); - stmt.setString(10, "92:ff:f5:ad:23:e1"); - stmt.setLong(11, dataCenterId); - stmt.setString(12, "2.2.4"); - stmt.setLong(13, 0); - stmt.setLong(14, 1238425896); - - boolean nfs = false; - if (url.startsWith("nfs")) { - nfs = true; - } - if (nfs) { - stmt.setString(15, "com.cloud.storage.resource.NfsSecondaryStorageResource"); - } else { - stmt.setString(15, "com.cloud.storage.secondary.LocalSecondaryStorageResource"); - } - stmt.setString(16, url); - stmt.setString(17, "None"); - stmt.executeUpdate(); + Transaction txn = Transaction.currentTxn(); + try { + PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql1); + stmt.setLong(1, 0); + stmt.setString(2, url); + stmt.setString(3, "UP"); + stmt.setString(4, "SecondaryStorage"); + stmt.setString(5, "192.168.122.1"); + stmt.setString(6, "255.255.255.0"); + stmt.setString(7, "92:ff:f5:ad:23:e1"); + stmt.setString(8, "192.168.122.1"); + stmt.setString(9, "255.255.255.0"); + stmt.setString(10, "92:ff:f5:ad:23:e1"); + stmt.setLong(11, dataCenterId); + stmt.setString(12, "2.2.4"); + stmt.setLong(13, 0); + stmt.setLong(14, 1238425896); - stmt = txn.prepareAutoCloseStatement(insertSqlHostDetails); - stmt.setLong(1, 1); - stmt.setLong(2, 1); - stmt.setString(3, "mount.parent"); - if (nfs) { - stmt.setString(4, "/mnt"); - } else { + boolean nfs = false; + if (url.startsWith("nfs")) { + nfs = true; + } + if (nfs) { + stmt.setString(15, "com.cloud.storage.resource.NfsSecondaryStorageResource"); + } else { + stmt.setString(15, "com.cloud.storage.secondary.LocalSecondaryStorageResource"); + } + stmt.setString(16, url); + stmt.setString(17, "None"); + stmt.executeUpdate(); + + stmt = txn.prepareAutoCloseStatement(insertSqlHostDetails); + stmt.setLong(1, 1); + stmt.setLong(2, 1); + stmt.setString(3, "mount.parent"); + if (nfs) { + stmt.setString(4, "/mnt"); + } else { stmt.setString(4, "/"); } - stmt.executeUpdate(); + stmt.executeUpdate(); - stmt.setLong(1, 2); - stmt.setLong(2, 1); - stmt.setString(3, "mount.path"); - if (nfs) { - stmt.setString(4, mountPoint); - } else { + stmt.setLong(1, 2); + stmt.setLong(2, 1); + stmt.setString(3, "mount.path"); + if (nfs) { + stmt.setString(4, mountPoint); + } else { stmt.setString(4, url.replaceFirst("file:/", "")); } - stmt.executeUpdate(); + stmt.executeUpdate(); + + stmt.setLong(1, 3); + stmt.setLong(2, 1); + stmt.setString(3, "orig.url"); + stmt.setString(4, url); + stmt.executeUpdate(); - stmt.setLong(1, 3); - stmt.setLong(2, 1); - stmt.setString(3, "orig.url"); - stmt.setString(4, url); - stmt.executeUpdate(); - stmt = txn.prepareAutoCloseStatement(insertSql2); stmt.setLong(1, 1); stmt.setLong(2, 1); stmt.executeUpdate(); - } catch (SQLException ex) { - System.out.println("Error creating secondary storage: " + ex.getMessage()); - return; - } + } catch (SQLException ex) { + System.out.println("Error creating secondary storage: " + ex.getMessage()); + return; + } } - + @DB public void saveCluster() { String name = _currentObjectParams.get("name"); @@ -562,7 +562,7 @@ public class DatabaseConfig { long podId = Long.parseLong(_currentObjectParams.get("podId")); String hypervisor = _currentObjectParams.get("hypervisorType"); String insertSql1 = "INSERT INTO `cluster` (`id`, `name`, `data_center_id` , `pod_id`, `hypervisor_type` , `cluster_type`, `allocation_state`) VALUES (?,?,?,?,?,?,?)"; - + Transaction txn = Transaction.currentTxn(); try { PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql1); @@ -583,54 +583,54 @@ public class DatabaseConfig { } - + @DB public void saveStoragePool() { - String name = _currentObjectParams.get("name"); - long id = Long.parseLong(_currentObjectParams.get("id")); - long dataCenterId = Long.parseLong(_currentObjectParams.get("zoneId")); - long podId = Long.parseLong(_currentObjectParams.get("podId")); - long clusterId = Long.parseLong(_currentObjectParams.get("clusterId")); - String hostAddress = _currentObjectParams.get("hostAddress"); - String hostPath = _currentObjectParams.get("hostPath"); - String storageType = _currentObjectParams.get("storageType"); - String uuid = UUID.nameUUIDFromBytes(new String(hostAddress+hostPath).getBytes()).toString(); - - String insertSql1 = "INSERT INTO `storage_pool` (`id`, `name`, `uuid` , `pool_type` , `port`, `data_center_id` ,`available_bytes` , `capacity_bytes` ,`host_address`, `path`, `created`, `pod_id`,`status` , `cluster_id`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; - // String insertSql2 = "INSERT INTO `netfs_storage_pool` VALUES (?,?,?)"; - - Transaction txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql1); - stmt.setLong(1, id); - stmt.setString(2, name); - stmt.setString(3, uuid); - if (storageType == null) { + String name = _currentObjectParams.get("name"); + long id = Long.parseLong(_currentObjectParams.get("id")); + long dataCenterId = Long.parseLong(_currentObjectParams.get("zoneId")); + long podId = Long.parseLong(_currentObjectParams.get("podId")); + long clusterId = Long.parseLong(_currentObjectParams.get("clusterId")); + String hostAddress = _currentObjectParams.get("hostAddress"); + String hostPath = _currentObjectParams.get("hostPath"); + String storageType = _currentObjectParams.get("storageType"); + String uuid = UUID.nameUUIDFromBytes(new String(hostAddress+hostPath).getBytes()).toString(); + + String insertSql1 = "INSERT INTO `storage_pool` (`id`, `name`, `uuid` , `pool_type` , `port`, `data_center_id` ,`available_bytes` , `capacity_bytes` ,`host_address`, `path`, `created`, `pod_id`,`status` , `cluster_id`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + // String insertSql2 = "INSERT INTO `netfs_storage_pool` VALUES (?,?,?)"; + + Transaction txn = Transaction.currentTxn(); + try { + PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql1); + stmt.setLong(1, id); + stmt.setString(2, name); + stmt.setString(3, uuid); + if (storageType == null) { stmt.setString(4, "NetworkFileSystem"); } else { stmt.setString(4, storageType); } - stmt.setLong(5, 111); - stmt.setLong(6, dataCenterId); - stmt.setLong(7,0); - stmt.setLong(8,0); - stmt.setString(9, hostAddress); - stmt.setString(10, hostPath); - stmt.setDate(11, new Date(new java.util.Date().getTime())); - stmt.setLong(12, podId); - stmt.setString(13, Status.Up.toString()); - stmt.setLong(14, clusterId); - stmt.executeUpdate(); + stmt.setLong(5, 111); + stmt.setLong(6, dataCenterId); + stmt.setLong(7,0); + stmt.setLong(8,0); + stmt.setString(9, hostAddress); + stmt.setString(10, hostPath); + stmt.setDate(11, new Date(new java.util.Date().getTime())); + stmt.setLong(12, podId); + stmt.setString(13, Status.Up.toString()); + stmt.setLong(14, clusterId); + stmt.executeUpdate(); - } catch (SQLException ex) { - System.out.println("Error creating storage pool: " + ex.getMessage()); - s_logger.error("error creating storage pool ", ex); - return; - } + } catch (SQLException ex) { + System.out.println("Error creating storage pool: " + ex.getMessage()); + s_logger.error("error creating storage pool ", ex); + return; + } - } + } - private void saveZone() { + private void saveZone() { long id = Long.parseLong(_currentObjectParams.get("id")); String name = _currentObjectParams.get("name"); //String description = _currentObjectParams.get("description"); @@ -641,7 +641,7 @@ public class DatabaseConfig { //String vnetRange = _currentObjectParams.get("vnet"); String guestNetworkCidr = _currentObjectParams.get("guestNetworkCidr"); String networkType = _currentObjectParams.get("networktype"); - + // Check that all IPs are valid String ipError = "Please enter a valid IP address for the field: "; if (!IPRangeConfig.validOrBlankIP(dns1)) { @@ -659,15 +659,15 @@ public class DatabaseConfig { if (!IPRangeConfig.validCIDR(guestNetworkCidr)) { printError("Please enter a valid value for guestNetworkCidr"); } - - pzc.saveZone(false, id, name, dns1, dns2, internalDns1, internalDns2, guestNetworkCidr, networkType); + + pzc.saveZone(false, id, name, dns1, dns2, internalDns1, internalDns2, guestNetworkCidr, networkType); } - + private void savePhysicalNetwork() { long id = Long.parseLong(_currentObjectParams.get("id")); String zoneId = _currentObjectParams.get("zoneId"); String vnetRange = _currentObjectParams.get("vnet"); - + int vnetStart = -1; int vnetEnd = -1; if (vnetRange != null) { @@ -677,16 +677,16 @@ public class DatabaseConfig { } long zoneDbId = Long.parseLong(zoneId); pzc.savePhysicalNetwork(false, id, zoneDbId, vnetStart, vnetEnd); - + } - + private void savePhysicalNetworkServiceProvider() { long id = Long.parseLong(_currentObjectParams.get("id")); long physicalNetworkId = Long.parseLong(_currentObjectParams.get("physicalNetworkId")); String providerName = _currentObjectParams.get("providerName"); long destPhysicalNetworkId = Long.parseLong(_currentObjectParams.get("destPhysicalNetworkId")); String uuid = UUID.randomUUID().toString(); - + int vpn = Integer.parseInt(_currentObjectParams.get("vpn")); int dhcp = Integer.parseInt(_currentObjectParams.get("dhcp")); int dns = Integer.parseInt(_currentObjectParams.get("dns")); @@ -698,12 +698,12 @@ public class DatabaseConfig { int pf =Integer.parseInt(_currentObjectParams.get("portForwarding")); int userData =Integer.parseInt(_currentObjectParams.get("userData")); int securityGroup =Integer.parseInt(_currentObjectParams.get("securityGroup")); - + String insertSql1 = "INSERT INTO `physical_network_service_providers` (`id`, `uuid`, `physical_network_id` , `provider_name`, `state` ," + - "`destination_physical_network_id`, `vpn_service_provided`, `dhcp_service_provided`, `dns_service_provided`, `gateway_service_provided`," + - "`firewall_service_provided`, `source_nat_service_provided`, `load_balance_service_provided`, `static_nat_service_provided`," + - "`port_forwarding_service_provided`, `user_data_service_provided`, `security_group_service_provided`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; - + "`destination_physical_network_id`, `vpn_service_provided`, `dhcp_service_provided`, `dns_service_provided`, `gateway_service_provided`," + + "`firewall_service_provided`, `source_nat_service_provided`, `load_balance_service_provided`, `static_nat_service_provided`," + + "`port_forwarding_service_provided`, `user_data_service_provided`, `security_group_service_provided`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + Transaction txn = Transaction.currentTxn(); try { PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql1); @@ -732,7 +732,7 @@ public class DatabaseConfig { } } - + private void saveVirtualRouterProvider() { long id = Long.parseLong(_currentObjectParams.get("id")); long nspId = Long.parseLong(_currentObjectParams.get("nspId")); @@ -740,7 +740,7 @@ public class DatabaseConfig { String type = _currentObjectParams.get("type"); String insertSql1 = "INSERT INTO `virtual_router_providers` (`id`, `nsp_id`, `uuid` , `type` , `enabled`) " + - "VALUES (?,?,?,?,?)"; + "VALUES (?,?,?,?,?)"; Transaction txn = Transaction.currentTxn(); try { @@ -758,18 +758,18 @@ public class DatabaseConfig { } } - + private void saveVlan() { - String zoneId = _currentObjectParams.get("zoneId"); - String physicalNetworkIdStr = _currentObjectParams.get("physicalNetworkId"); - String vlanId = _currentObjectParams.get("vlanId"); - String gateway = _currentObjectParams.get("gateway"); + String zoneId = _currentObjectParams.get("zoneId"); + String physicalNetworkIdStr = _currentObjectParams.get("physicalNetworkId"); + String vlanId = _currentObjectParams.get("vlanId"); + String gateway = _currentObjectParams.get("gateway"); String netmask = _currentObjectParams.get("netmask"); String publicIpRange = _currentObjectParams.get("ipAddressRange"); String vlanType = _currentObjectParams.get("vlanType"); String vlanPodName = _currentObjectParams.get("podName"); - - + + String ipError = "Please enter a valid IP address for the field: "; if (!IPRangeConfig.validOrBlankIP(gateway)) { printError(ipError + "gateway"); @@ -777,51 +777,51 @@ public class DatabaseConfig { if (!IPRangeConfig.validOrBlankIP(netmask)) { printError(ipError + "netmask"); } - + // Check that the given IP address range was valid - if (!checkIpAddressRange(publicIpRange)) { + if (!checkIpAddressRange(publicIpRange)) { printError("Please enter a valid public IP range."); } - - // Split the IP address range - String[] ipAddressRangeArray = publicIpRange.split("\\-"); - String startIP = ipAddressRangeArray[0]; - String endIP = null; - if (ipAddressRangeArray.length > 1) { + + // Split the IP address range + String[] ipAddressRangeArray = publicIpRange.split("\\-"); + String startIP = ipAddressRangeArray[0]; + String endIP = null; + if (ipAddressRangeArray.length > 1) { endIP = ipAddressRangeArray[1]; } - - // If a netmask was provided, check that the startIP, endIP, and gateway all belong to the same subnet - if (netmask != null && netmask != "") { - if (endIP != null) { - if (!IPRangeConfig.sameSubnet(startIP, endIP, netmask)) { + + // If a netmask was provided, check that the startIP, endIP, and gateway all belong to the same subnet + if (netmask != null && netmask != "") { + if (endIP != null) { + if (!IPRangeConfig.sameSubnet(startIP, endIP, netmask)) { printError("Start and end IPs for the public IP range must be in the same subnet, as per the provided netmask."); } - } - - if (gateway != null && gateway != "") { - if (!IPRangeConfig.sameSubnet(startIP, gateway, netmask)) { + } + + if (gateway != null && gateway != "") { + if (!IPRangeConfig.sameSubnet(startIP, gateway, netmask)) { printError("The start IP for the public IP range must be in the same subnet as the gateway, as per the provided netmask."); } - if (endIP != null) { - if (!IPRangeConfig.sameSubnet(endIP, gateway, netmask)) { + if (endIP != null) { + if (!IPRangeConfig.sameSubnet(endIP, gateway, netmask)) { printError("The end IP for the public IP range must be in the same subnet as the gateway, as per the provided netmask."); } - } - } - } - - long zoneDbId = Long.parseLong(zoneId); - String zoneName = PodZoneConfig.getZoneName(zoneDbId); - - long physicalNetworkId = Long.parseLong(physicalNetworkIdStr); - - //Set networkId to be 0, the value will be updated after management server starts up - pzc.modifyVlan(zoneName, true, vlanId, gateway, netmask, vlanPodName, vlanType, publicIpRange, 0, physicalNetworkId); - - long vlanDbId = pzc.getVlanDbId(zoneName, vlanId); - iprc.saveIPRange("public", -1, zoneDbId, vlanDbId, startIP, endIP, null, physicalNetworkId); - + } + } + } + + long zoneDbId = Long.parseLong(zoneId); + String zoneName = PodZoneConfig.getZoneName(zoneDbId); + + long physicalNetworkId = Long.parseLong(physicalNetworkIdStr); + + //Set networkId to be 0, the value will be updated after management server starts up + pzc.modifyVlan(zoneName, true, vlanId, gateway, netmask, vlanPodName, vlanType, publicIpRange, 0, physicalNetworkId); + + long vlanDbId = pzc.getVlanDbId(zoneName, vlanId); + iprc.saveIPRange("public", -1, zoneDbId, vlanDbId, startIP, endIP, null, physicalNetworkId); + } private void savePod() { @@ -835,7 +835,7 @@ public class DatabaseConfig { String startIP = null; String endIP = null; String vlanRange = _currentObjectParams.get("vnet"); - + int vlanStart = -1; int vlanEnd = -1; if (vlanRange != null) { @@ -843,51 +843,51 @@ public class DatabaseConfig { vlanStart = Integer.parseInt(tokens[0]); vlanEnd = Integer.parseInt(tokens[1]); } - + // Get the individual cidrAddress and cidrSize values - String[] cidrPair = cidr.split("\\/"); - String cidrAddress = cidrPair[0]; - String cidrSize = cidrPair[1]; + String[] cidrPair = cidr.split("\\/"); + String cidrAddress = cidrPair[0]; + String cidrSize = cidrPair[1]; long cidrSizeNum = Long.parseLong(cidrSize); - + // Check that the gateway is in the same subnet as the CIDR - if (!IPRangeConfig.sameSubnetCIDR(gateway, cidrAddress, cidrSizeNum)) { - printError("For pod " + name + " in zone " + zoneName + " , please ensure that your gateway is in the same subnet as the pod's CIDR address."); - } - + if (!IPRangeConfig.sameSubnetCIDR(gateway, cidrAddress, cidrSizeNum)) { + printError("For pod " + name + " in zone " + zoneName + " , please ensure that your gateway is in the same subnet as the pod's CIDR address."); + } + pzc.savePod(false, id, name, dataCenterId, gateway, cidr, vlanStart, vlanEnd); - - if (privateIpRange != null) { - // Check that the given IP address range was valid - if (!checkIpAddressRange(privateIpRange)) { + + if (privateIpRange != null) { + // Check that the given IP address range was valid + if (!checkIpAddressRange(privateIpRange)) { printError("Please enter a valid private IP range."); } - - String[] ipAddressRangeArray = privateIpRange.split("\\-"); - startIP = ipAddressRangeArray[0]; - endIP = null; - if (ipAddressRangeArray.length > 1) { + + String[] ipAddressRangeArray = privateIpRange.split("\\-"); + startIP = ipAddressRangeArray[0]; + endIP = null; + if (ipAddressRangeArray.length > 1) { endIP = ipAddressRangeArray[1]; } - } - - // Check that the start IP and end IP match up with the CIDR - if (!IPRangeConfig.sameSubnetCIDR(startIP, endIP, cidrSizeNum)) { - printError("For pod " + name + " in zone " + zoneName + ", please ensure that your start IP and end IP are in the same subnet, as per the pod's CIDR size."); - } - - if (!IPRangeConfig.sameSubnetCIDR(startIP, cidrAddress, cidrSizeNum)) { - printError("For pod " + name + " in zone " + zoneName + ", please ensure that your start IP is in the same subnet as the pod's CIDR address."); - } - - if (!IPRangeConfig.sameSubnetCIDR(endIP, cidrAddress, cidrSizeNum)) { - printError("For pod " + name + " in zone " + zoneName + ", please ensure that your end IP is in the same subnet as the pod's CIDR address."); - } - - if (privateIpRange != null) { - // Save the IP address range - iprc.saveIPRange("private", id, dataCenterId, -1, startIP, endIP, null, -1); - } + } + + // Check that the start IP and end IP match up with the CIDR + if (!IPRangeConfig.sameSubnetCIDR(startIP, endIP, cidrSizeNum)) { + printError("For pod " + name + " in zone " + zoneName + ", please ensure that your start IP and end IP are in the same subnet, as per the pod's CIDR size."); + } + + if (!IPRangeConfig.sameSubnetCIDR(startIP, cidrAddress, cidrSizeNum)) { + printError("For pod " + name + " in zone " + zoneName + ", please ensure that your start IP is in the same subnet as the pod's CIDR address."); + } + + if (!IPRangeConfig.sameSubnetCIDR(endIP, cidrAddress, cidrSizeNum)) { + printError("For pod " + name + " in zone " + zoneName + ", please ensure that your end IP is in the same subnet as the pod's CIDR address."); + } + + if (privateIpRange != null) { + // Save the IP address range + iprc.saveIPRange("private", id, dataCenterId, -1, startIP, endIP, null, -1); + } } @@ -900,30 +900,30 @@ public class DatabaseConfig { int ramSize = Integer.parseInt(_currentObjectParams.get("ramSize")); int speed = Integer.parseInt(_currentObjectParams.get("speed")); String useLocalStorageValue = _currentObjectParams.get("useLocalStorage"); - + // int nwRate = Integer.parseInt(_currentObjectParams.get("nwRate")); // int mcRate = Integer.parseInt(_currentObjectParams.get("mcRate")); boolean ha = Boolean.parseBoolean(_currentObjectParams.get("enableHA")); boolean mirroring = Boolean.parseBoolean(_currentObjectParams.get("mirrored")); - + boolean useLocalStorage; if (useLocalStorageValue != null) { - if (Boolean.parseBoolean(useLocalStorageValue)) { - useLocalStorage = true; - } else { - useLocalStorage = false; - } + if (Boolean.parseBoolean(useLocalStorageValue)) { + useLocalStorage = true; + } else { + useLocalStorage = false; + } } else { - useLocalStorage = false; + useLocalStorage = false; } - + ServiceOfferingVO serviceOffering = new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, useLocalStorage, false, null, false, null, false); - ServiceOfferingDaoImpl dao = LegacyComponentLocator.inject(ServiceOfferingDaoImpl.class); + ServiceOfferingDaoImpl dao = ComponentContext.inject(ServiceOfferingDaoImpl.class); try { dao.persist(serviceOffering); } catch (Exception e) { s_logger.error("error creating service offering", e); - + } /* String insertSql = "INSERT INTO `cloud`.`service_offering` (id, name, cpu, ram_size, speed, nw_rate, mc_rate, created, ha_enabled, mirrored, display_text, guest_ip_type, use_local_storage) " + @@ -937,9 +937,9 @@ public class DatabaseConfig { s_logger.error("error creating service offering", ex); return; } - */ + */ } - + @DB protected void saveDiskOffering() { long id = Long.parseLong(_currentObjectParams.get("id")); @@ -953,9 +953,9 @@ public class DatabaseConfig { String useLocal = _currentObjectParams.get("useLocal"); boolean local = false; if (useLocal != null) { - local = Boolean.parseBoolean(useLocal); + local = Boolean.parseBoolean(useLocal); } - + if (tags != null && tags.length() > 0) { String[] tokens = tags.split(","); StringBuilder newTags = new StringBuilder(); @@ -967,12 +967,12 @@ public class DatabaseConfig { } DiskOfferingVO diskOffering = new DiskOfferingVO(domainId, name, displayText, diskSpace , tags, false); diskOffering.setUseLocalStorage(local); - DiskOfferingDaoImpl offering = LegacyComponentLocator.inject(DiskOfferingDaoImpl.class); + DiskOfferingDaoImpl offering = ComponentContext.inject(DiskOfferingDaoImpl.class); try { offering.persist(diskOffering); } catch (Exception e) { s_logger.error("error creating disk offering", e); - + } /* String insertSql = "INSERT INTO `cloud`.`disk_offering` (id, domain_id, name, display_text, disk_size, mirrored, tags) " + @@ -987,37 +987,37 @@ public class DatabaseConfig { s_logger.error("error creating disk offering", ex); return; } - */ + */ } - + @DB protected void saveThrottlingRates() { - boolean saveNetworkThrottlingRate = (_networkThrottlingRate != null); - boolean saveMulticastThrottlingRate = (_multicastThrottlingRate != null); - - if (!saveNetworkThrottlingRate && !saveMulticastThrottlingRate) { + boolean saveNetworkThrottlingRate = (_networkThrottlingRate != null); + boolean saveMulticastThrottlingRate = (_multicastThrottlingRate != null); + + if (!saveNetworkThrottlingRate && !saveMulticastThrottlingRate) { return; } - - String insertNWRateSql = "UPDATE `cloud`.`service_offering` SET `nw_rate` = ?"; - String insertMCRateSql = "UPDATE `cloud`.`service_offering` SET `mc_rate` = ?"; - + + String insertNWRateSql = "UPDATE `cloud`.`service_offering` SET `nw_rate` = ?"; + String insertMCRateSql = "UPDATE `cloud`.`service_offering` SET `mc_rate` = ?"; + Transaction txn = Transaction.currentTxn(); - try { + try { PreparedStatement stmt; - + if (saveNetworkThrottlingRate) { - stmt = txn.prepareAutoCloseStatement(insertNWRateSql); - stmt.setString(1, _networkThrottlingRate); - stmt.executeUpdate(); + stmt = txn.prepareAutoCloseStatement(insertNWRateSql); + stmt.setString(1, _networkThrottlingRate); + stmt.executeUpdate(); } - + if (saveMulticastThrottlingRate) { - stmt = txn.prepareAutoCloseStatement(insertMCRateSql); - stmt.setString(1, _multicastThrottlingRate); - stmt.executeUpdate(); + stmt = txn.prepareAutoCloseStatement(insertMCRateSql); + stmt.setString(1, _multicastThrottlingRate); + stmt.executeUpdate(); } - + } catch (SQLException ex) { s_logger.error("error saving network and multicast throttling rates to all service offerings", ex); return; @@ -1026,7 +1026,7 @@ public class DatabaseConfig { // no configurable values for VM Template, hard-code the defaults for now private void saveVMTemplate() { - /* + /* long id = 1; String uniqueName = "routing"; String name = "DomR Template"; @@ -1051,8 +1051,8 @@ public class DatabaseConfig { } finally { txn.close(); } - */ -/* + */ + /* // do it again for console proxy template id = 2; uniqueName = "consoleproxy"; @@ -1060,7 +1060,7 @@ public class DatabaseConfig { isPublic = 0; path = "template/private/u000000/os/consoleproxy"; type = "ext3"; - + insertSql = "INSERT INTO `cloud`.`vm_template` (id, unique_name, name, public, path, created, type, hvm, bits, created_by, ready) " + "VALUES (" + id + ",'" + uniqueName + "','" + name + "'," + isPublic + ",'" + path + "',now(),'" + type + "'," + requiresHvm + "," + bits + "," + createdByUserId + "," + isReady + ")"; @@ -1074,7 +1074,7 @@ public class DatabaseConfig { } finally { txn.close(); } -*/ + */ } @DB @@ -1091,27 +1091,27 @@ public class DatabaseConfig { // insert system user insertSql = "INSERT INTO `cloud`.`user` (id, username, password, account_id, firstname, lastname, created)" + - " VALUES (1, 'system', RAND(), 1, 'system', 'cloud', now())"; - txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql); - stmt.executeUpdate(); - } catch (SQLException ex) { - s_logger.error("error creating system user", ex); - } - - // insert admin user + " VALUES (1, 'system', RAND(), 1, 'system', 'cloud', now())"; + txn = Transaction.currentTxn(); + try { + PreparedStatement stmt = txn.prepareAutoCloseStatement(insertSql); + stmt.executeUpdate(); + } catch (SQLException ex) { + s_logger.error("error creating system user", ex); + } + + // insert admin user long id = Long.parseLong(_currentObjectParams.get("id")); String username = _currentObjectParams.get("username"); String firstname = _currentObjectParams.get("firstname"); String lastname = _currentObjectParams.get("lastname"); String password = _currentObjectParams.get("password"); String email = _currentObjectParams.get("email"); - + if (email == null || email.equals("")) { printError("An email address for each user is required."); } - + MessageDigest md5 = null; try { md5 = MessageDigest.getInstance("MD5"); @@ -1158,45 +1158,45 @@ public class DatabaseConfig { saveConfiguration(name, value, null); } } - + private void saveConfiguration() { String name = _currentObjectParams.get("name"); String value = _currentObjectParams.get("value"); String category = _currentObjectParams.get("category"); saveConfiguration(name, value, category); } - + @DB protected void saveConfiguration(String name, String value, String category) { String instance = "DEFAULT"; String description = s_configurationDescriptions.get(name); String component = s_configurationComponents.get(name); if (category == null) { - category = "Advanced"; + category = "Advanced"; } - + String instanceNameError = "Please enter a non-blank value for the field: "; if (name.equals("instance.name")) { - if (value == null || value.isEmpty() || !value.matches("^[A-Za-z0-9]{1,8}$")) { + if (value == null || value.isEmpty() || !value.matches("^[A-Za-z0-9]{1,8}$")) { printError(instanceNameError + "configuration: instance.name can not be empty and can only contain numbers and alphabets up to 8 characters long"); } } - + if (name.equals("network.throttling.rate")) { - if (value != null && !value.isEmpty()) { + if (value != null && !value.isEmpty()) { _networkThrottlingRate = value; } } - + if (name.equals("multicast.throttling.rate")) { - if (value != null && !value.isEmpty()) { + if (value != null && !value.isEmpty()) { _multicastThrottlingRate = value; } } String insertSql = "INSERT INTO `cloud`.`configuration` (instance, component, name, value, description, category) " + - "VALUES ('" + instance + "','" + component + "','" + name + "','" + value + "','" + description + "','" + category + "')"; - + "VALUES ('" + instance + "','" + component + "','" + name + "','" + value + "','" + description + "','" + category + "')"; + String selectSql = "SELECT name FROM cloud.configuration WHERE name = '" + name + "'"; Transaction txn = Transaction.currentTxn(); @@ -1205,38 +1205,38 @@ public class DatabaseConfig { ResultSet result = stmt.executeQuery(); Boolean hasRow = result.next(); if (!hasRow) { - stmt = txn.prepareAutoCloseStatement(insertSql); - stmt.executeUpdate(); + stmt = txn.prepareAutoCloseStatement(insertSql); + stmt.executeUpdate(); } } catch (SQLException ex) { s_logger.error("error creating configuration", ex); } } - + private boolean checkIpAddressRange(String ipAddressRange) { - String[] ipAddressRangeArray = ipAddressRange.split("\\-"); - String startIP = ipAddressRangeArray[0]; - String endIP = null; - if (ipAddressRangeArray.length > 1) { + String[] ipAddressRangeArray = ipAddressRange.split("\\-"); + String startIP = ipAddressRangeArray[0]; + String endIP = null; + if (ipAddressRangeArray.length > 1) { endIP = ipAddressRangeArray[1]; } - - if (!IPRangeConfig.validIP(startIP)) { - s_logger.error("The private IP address: " + startIP + " is invalid."); - return false; - } - - if (!IPRangeConfig.validOrBlankIP(endIP)) { - s_logger.error("The private IP address: " + endIP + " is invalid."); - return false; - } - - if (!IPRangeConfig.validIPRange(startIP, endIP)) { - s_logger.error("The IP range " + startIP + " -> " + endIP + " is invalid."); - return false; - } - - return true; + + if (!IPRangeConfig.validIP(startIP)) { + s_logger.error("The private IP address: " + startIP + " is invalid."); + return false; + } + + if (!IPRangeConfig.validOrBlankIP(endIP)) { + s_logger.error("The private IP address: " + endIP + " is invalid."); + return false; + } + + if (!IPRangeConfig.validIPRange(startIP, endIP)) { + s_logger.error("The IP range " + startIP + " -> " + endIP + " is invalid."); + return false; + } + + return true; } @DB @@ -1249,7 +1249,7 @@ public class DatabaseConfig { } catch (SQLException ex) { s_logger.error("error creating ROOT domain", ex); } - + /* String updateSql = "update account set domain_id = 1 where id = 2"; Transaction txn = Transaction.currentTxn(); @@ -1272,7 +1272,7 @@ public class DatabaseConfig { } finally { txn.close(); } - */ + */ } class DbConfigXMLHandler extends DefaultHandler { @@ -1295,14 +1295,14 @@ public class DatabaseConfig { @Override public void startElement(String s, String s1, String s2, Attributes attributes) throws SAXException { - if ("object".equals(s2)) { - _parent.setCurrentObjectName(convertName(attributes.getValue("name"))); - } else if ("field".equals(s2)) { - if (_currentObjectParams == null) { - _currentObjectParams = new HashMap(); - } - _currentFieldName = convertName(attributes.getValue("name")); - } else if (DatabaseConfig.objectNames.contains(s2)) { + if ("object".equals(s2)) { + _parent.setCurrentObjectName(convertName(attributes.getValue("name"))); + } else if ("field".equals(s2)) { + if (_currentObjectParams == null) { + _currentObjectParams = new HashMap(); + } + _currentFieldName = convertName(attributes.getValue("name")); + } else if (DatabaseConfig.objectNames.contains(s2)) { _parent.setCurrentObjectName(s2); } else if (DatabaseConfig.fieldNames.contains(s2)) { if (_currentObjectParams == null) { @@ -1313,95 +1313,95 @@ public class DatabaseConfig { } @Override - public void characters(char[] ch, int start, int length) throws SAXException { + public void characters(char[] ch, int start, int length) throws SAXException { if ((_currentObjectParams != null) && (_currentFieldName != null)) { String currentFieldVal = new String(ch, start, length); _currentObjectParams.put(_currentFieldName, currentFieldVal); } } - + private String convertName(String name) { - if (name.contains(".")){ - String[] nameArray = name.split("\\."); - for (int i = 1; i < nameArray.length; i++) { - String word = nameArray[i]; - nameArray[i] = word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase(); - } - name = ""; - for (int i = 0; i < nameArray.length; i++) { - name = name.concat(nameArray[i]); - } - } - return name; - } + if (name.contains(".")){ + String[] nameArray = name.split("\\."); + for (int i = 1; i < nameArray.length; i++) { + String word = nameArray[i]; + nameArray[i] = word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase(); + } + name = ""; + for (int i = 0; i < nameArray.length; i++) { + name = name.concat(nameArray[i]); + } + } + return name; + } } - + public static List genReturnList(String success, String message) { - List returnList = new ArrayList(2); - returnList.add(0, success); - returnList.add(1, message); - return returnList; - } - + List returnList = new ArrayList(2); + returnList.add(0, success); + returnList.add(1, message); + return returnList; + } + public static void printError(String message) { - System.out.println(message); - System.exit(1); + System.out.println(message); + System.exit(1); } public static String getDatabaseValueString(String selectSql, String name, String errorMsg) { - Transaction txn = Transaction.open("getDatabaseValueString"); - PreparedStatement stmt = null; - - try { - stmt = txn.prepareAutoCloseStatement(selectSql); - ResultSet rs = stmt.executeQuery(); - if (rs.next()) { - String value = rs.getString(name); - return value; - } else { + Transaction txn = Transaction.open("getDatabaseValueString"); + PreparedStatement stmt = null; + + try { + stmt = txn.prepareAutoCloseStatement(selectSql); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + String value = rs.getString(name); + return value; + } else { return null; } - } catch (SQLException e) { - System.out.println("Exception: " + e.getMessage()); - printError(errorMsg); - } finally { - txn.close(); - } - return null; - } - - public static long getDatabaseValueLong(String selectSql, String name, String errorMsg) { - Transaction txn = Transaction.open("getDatabaseValueLong"); - PreparedStatement stmt = null; - - try { - stmt = txn.prepareAutoCloseStatement(selectSql); - ResultSet rs = stmt.executeQuery(); - if (rs.next()) { - return rs.getLong(name); - } else { - return -1; - } - } catch (SQLException e) { - System.out.println("Exception: " + e.getMessage()); - printError(errorMsg); - } finally { - txn.close(); - } - return -1; - } - - public static void saveSQL(String sql, String errorMsg) { - Transaction txn = Transaction.open("saveSQL"); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(sql); - stmt.executeUpdate(); - } catch (SQLException ex) { - System.out.println("SQL Exception: " + ex.getMessage()); + } catch (SQLException e) { + System.out.println("Exception: " + e.getMessage()); printError(errorMsg); } finally { txn.close(); } - } - + return null; + } + + public static long getDatabaseValueLong(String selectSql, String name, String errorMsg) { + Transaction txn = Transaction.open("getDatabaseValueLong"); + PreparedStatement stmt = null; + + try { + stmt = txn.prepareAutoCloseStatement(selectSql); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + return rs.getLong(name); + } else { + return -1; + } + } catch (SQLException e) { + System.out.println("Exception: " + e.getMessage()); + printError(errorMsg); + } finally { + txn.close(); + } + return -1; + } + + public static void saveSQL(String sql, String errorMsg) { + Transaction txn = Transaction.open("saveSQL"); + try { + PreparedStatement stmt = txn.prepareAutoCloseStatement(sql); + stmt.executeUpdate(); + } catch (SQLException ex) { + System.out.println("SQL Exception: " + ex.getMessage()); + printError(errorMsg); + } finally { + txn.close(); + } + } + } diff --git a/server/src/com/cloud/test/IPRangeConfig.java b/server/src/com/cloud/test/IPRangeConfig.java index 1f1f90f006a..4b884f8c4b2 100755 --- a/server/src/com/cloud/test/IPRangeConfig.java +++ b/server/src/com/cloud/test/IPRangeConfig.java @@ -26,482 +26,482 @@ import java.util.List; import java.util.UUID; import java.util.Vector; - +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; import com.cloud.utils.net.NetUtils; public class IPRangeConfig { - - public static void main(String[] args) { - IPRangeConfig config = ComponentLocator.inject(IPRangeConfig.class); - config.run(args); - System.exit(0); + + public static void main(String[] args) { + IPRangeConfig config = ComponentContext.inject(IPRangeConfig.class); + config.run(args); + System.exit(0); } - - private String usage() { - return "Usage: ./change_ip_range.sh [add|delete] [public zone | private pod zone] startIP endIP"; - } - - - public void run(String[] args) { - if (args.length < 2) { + + private String usage() { + return "Usage: ./change_ip_range.sh [add|delete] [public zone | private pod zone] startIP endIP"; + } + + + public void run(String[] args) { + if (args.length < 2) { printError(usage()); } - - String op = args[0]; - String type = args[1]; - - if (type.equals("public")) { - if (args.length != 4 && args.length != 5) { + + String op = args[0]; + String type = args[1]; + + if (type.equals("public")) { + if (args.length != 4 && args.length != 5) { printError(usage()); } - String zone = args[2]; - String startIP = args[3]; - String endIP = null; - if (args.length == 5) { + String zone = args[2]; + String startIP = args[3]; + String endIP = null; + if (args.length == 5) { endIP = args[4]; } - - String result = checkErrors(type, op, null, zone, startIP, endIP); - if (!result.equals("success")) { + + String result = checkErrors(type, op, null, zone, startIP, endIP); + if (!result.equals("success")) { printError(result); } - - long zoneId = PodZoneConfig.getZoneId(zone); - result = changeRange(op, "public", -1, zoneId, startIP, endIP, null, -1); - result.replaceAll("
", "/n"); - System.out.println(result); - } else if (type.equals("private")) { - if (args.length != 5 && args.length != 6) { + + long zoneId = PodZoneConfig.getZoneId(zone); + result = changeRange(op, "public", -1, zoneId, startIP, endIP, null, -1); + result.replaceAll("
", "/n"); + System.out.println(result); + } else if (type.equals("private")) { + if (args.length != 5 && args.length != 6) { printError(usage()); } - String pod = args[2]; - String zone = args[3];; - String startIP = args[4]; - String endIP = null; - if (args.length == 6) { + String pod = args[2]; + String zone = args[3];; + String startIP = args[4]; + String endIP = null; + if (args.length == 6) { endIP = args[5]; } - - String result = checkErrors(type, op, pod, zone, startIP, endIP); - if (!result.equals("success")) { + + String result = checkErrors(type, op, pod, zone, startIP, endIP); + if (!result.equals("success")) { printError(result); } - - long podId = PodZoneConfig.getPodId(pod, zone); - long zoneId = PodZoneConfig.getZoneId(zone); - result = changeRange(op, "private", podId, zoneId, startIP, endIP, null, -1); - result.replaceAll("
", "/n"); - System.out.println(result); - } else { - printError(usage()); - } - } - - public List changePublicIPRangeGUI(String op, String zone, String startIP, String endIP, long physicalNetworkId) { - String result = checkErrors("public", op, null, zone, startIP, endIP); - if (!result.equals("success")) { - return DatabaseConfig.genReturnList("false", result); - } - - long zoneId = PodZoneConfig.getZoneId(zone); - result = changeRange(op, "public", -1, zoneId, startIP, endIP, null, physicalNetworkId); - - return DatabaseConfig.genReturnList("true", result); - } - - public List changePrivateIPRangeGUI(String op, String pod, String zone, String startIP, String endIP) { - String result = checkErrors("private", op, pod, zone, startIP, endIP); - if (!result.equals("success")) { - return DatabaseConfig.genReturnList("false", result); - } - - long podId = PodZoneConfig.getPodId(pod, zone); - long zoneId = PodZoneConfig.getZoneId(zone); - result = changeRange(op, "private", podId, zoneId, startIP, endIP, null, -1); - - return DatabaseConfig.genReturnList("true", result); - } - private String checkErrors(String type, String op, String pod, String zone, String startIP, String endIP) { - if (!op.equals("add") && !op.equals("delete")) { + long podId = PodZoneConfig.getPodId(pod, zone); + long zoneId = PodZoneConfig.getZoneId(zone); + result = changeRange(op, "private", podId, zoneId, startIP, endIP, null, -1); + result.replaceAll("
", "/n"); + System.out.println(result); + } else { + printError(usage()); + } + } + + public List changePublicIPRangeGUI(String op, String zone, String startIP, String endIP, long physicalNetworkId) { + String result = checkErrors("public", op, null, zone, startIP, endIP); + if (!result.equals("success")) { + return DatabaseConfig.genReturnList("false", result); + } + + long zoneId = PodZoneConfig.getZoneId(zone); + result = changeRange(op, "public", -1, zoneId, startIP, endIP, null, physicalNetworkId); + + return DatabaseConfig.genReturnList("true", result); + } + + public List changePrivateIPRangeGUI(String op, String pod, String zone, String startIP, String endIP) { + String result = checkErrors("private", op, pod, zone, startIP, endIP); + if (!result.equals("success")) { + return DatabaseConfig.genReturnList("false", result); + } + + long podId = PodZoneConfig.getPodId(pod, zone); + long zoneId = PodZoneConfig.getZoneId(zone); + result = changeRange(op, "private", podId, zoneId, startIP, endIP, null, -1); + + return DatabaseConfig.genReturnList("true", result); + } + + private String checkErrors(String type, String op, String pod, String zone, String startIP, String endIP) { + if (!op.equals("add") && !op.equals("delete")) { return usage(); } - - if (type.equals("public")) { - // Check that the zone is valid - if (!PodZoneConfig.validZone(zone)) { + + if (type.equals("public")) { + // Check that the zone is valid + if (!PodZoneConfig.validZone(zone)) { return "Please specify a valid zone."; } - } else if (type.equals("private")) { - // Check that the pod and zone are valid - if (!PodZoneConfig.validZone(zone)) { + } else if (type.equals("private")) { + // Check that the pod and zone are valid + if (!PodZoneConfig.validZone(zone)) { return "Please specify a valid zone."; } - if (!PodZoneConfig.validPod(pod, zone)) { + if (!PodZoneConfig.validPod(pod, zone)) { return "Please specify a valid pod."; } - } - - if (!validIP(startIP)) { + } + + if (!validIP(startIP)) { return "Please specify a valid start IP"; } - - if (!validOrBlankIP(endIP)) { + + if (!validOrBlankIP(endIP)) { return "Please specify a valid end IP"; } - - // Check that the IPs that are being added are compatible with either the zone's public netmask, or the pod's CIDR - if (type.equals("public")) { - // String publicNetmask = getPublicNetmask(zone); - // String publicGateway = getPublicGateway(zone); - - // if (publicNetmask == null) return "Please ensure that your zone's public net mask is specified"; - // if (!sameSubnet(startIP, endIP, publicNetmask)) return "Please ensure that your start IP and end IP are in the same subnet, as per the zone's netmask."; - // if (!sameSubnet(startIP, publicGateway, publicNetmask)) return "Please ensure that your start IP is in the same subnet as your zone's gateway, as per the zone's netmask."; - // if (!sameSubnet(endIP, publicGateway, publicNetmask)) return "Please ensure that your end IP is in the same subnet as your zone's gateway, as per the zone's netmask."; - } else if (type.equals("private")) { - String cidrAddress = getCidrAddress(pod, zone); - long cidrSize = getCidrSize(pod, zone); - if (!sameSubnetCIDR(startIP, endIP, cidrSize)) { + // Check that the IPs that are being added are compatible with either the zone's public netmask, or the pod's CIDR + if (type.equals("public")) { + // String publicNetmask = getPublicNetmask(zone); + // String publicGateway = getPublicGateway(zone); + + // if (publicNetmask == null) return "Please ensure that your zone's public net mask is specified"; + // if (!sameSubnet(startIP, endIP, publicNetmask)) return "Please ensure that your start IP and end IP are in the same subnet, as per the zone's netmask."; + // if (!sameSubnet(startIP, publicGateway, publicNetmask)) return "Please ensure that your start IP is in the same subnet as your zone's gateway, as per the zone's netmask."; + // if (!sameSubnet(endIP, publicGateway, publicNetmask)) return "Please ensure that your end IP is in the same subnet as your zone's gateway, as per the zone's netmask."; + } else if (type.equals("private")) { + String cidrAddress = getCidrAddress(pod, zone); + long cidrSize = getCidrSize(pod, zone); + + if (!sameSubnetCIDR(startIP, endIP, cidrSize)) { return "Please ensure that your start IP and end IP are in the same subnet, as per the pod's CIDR size."; } - if (!sameSubnetCIDR(startIP, cidrAddress, cidrSize)) { + if (!sameSubnetCIDR(startIP, cidrAddress, cidrSize)) { return "Please ensure that your start IP is in the same subnet as the pod's CIDR address."; } - if (!sameSubnetCIDR(endIP, cidrAddress, cidrSize)) { + if (!sameSubnetCIDR(endIP, cidrAddress, cidrSize)) { return "Please ensure that your end IP is in the same subnet as the pod's CIDR address."; } - } - - if (!validIPRange(startIP, endIP)) { + } + + if (!validIPRange(startIP, endIP)) { return "Please specify a valid IP range."; } - - return "success"; - } - - private String genChangeRangeSuccessString(List problemIPs, String op) { - if (problemIPs == null) { + + return "success"; + } + + private String genChangeRangeSuccessString(List problemIPs, String op) { + if (problemIPs == null) { return ""; } - - if (problemIPs.size() == 0) { - if (op.equals("add")) { + + if (problemIPs.size() == 0) { + if (op.equals("add")) { return "Successfully added all IPs in the specified range."; } else if (op.equals("delete")) { return "Successfully deleted all IPs in the specified range."; } else { return ""; } - } else { - String successString = ""; - if (op.equals("add")) { + } else { + String successString = ""; + if (op.equals("add")) { successString += "Failed to add the following IPs, because they are already in the database:

"; } else if (op.equals("delete")) { successString += "Failed to delete the following IPs, because they are in use:

"; } - - for (int i = 0; i < problemIPs.size(); i++) { - successString += problemIPs.get(i); - if (i != (problemIPs.size() - 1)) { + + for (int i = 0; i < problemIPs.size(); i++) { + successString += problemIPs.get(i); + if (i != (problemIPs.size() - 1)) { successString += ", "; } - } - - successString += "

"; - - if (op.equals("add")) { + } + + successString += "

"; + + if (op.equals("add")) { successString += "Successfully added all other IPs in the specified range."; } else if (op.equals("delete")) { successString += "Successfully deleted all other IPs in the specified range."; } - - return successString; - } - } - - private String changeRange(String op, String type, long podId, long zoneId, String startIP, String endIP, Long networkId, long physicalNetworkId) { - - // Go through all the IPs and add or delete them - List problemIPs = null; - if (op.equals("add")) { - problemIPs = saveIPRange(type, podId, zoneId, 1, startIP, endIP, networkId, physicalNetworkId); - } else if (op.equals("delete")) { - problemIPs = deleteIPRange(type, podId, zoneId, 1, startIP, endIP); - } - - if (problemIPs == null) { + + return successString; + } + } + + private String changeRange(String op, String type, long podId, long zoneId, String startIP, String endIP, Long networkId, long physicalNetworkId) { + + // Go through all the IPs and add or delete them + List problemIPs = null; + if (op.equals("add")) { + problemIPs = saveIPRange(type, podId, zoneId, 1, startIP, endIP, networkId, physicalNetworkId); + } else if (op.equals("delete")) { + problemIPs = deleteIPRange(type, podId, zoneId, 1, startIP, endIP); + } + + if (problemIPs == null) { return null; } else { return genChangeRangeSuccessString(problemIPs, op); } - } - - private String genSuccessString(Vector problemIPs, String op) { - if (problemIPs == null) { + } + + private String genSuccessString(Vector problemIPs, String op) { + if (problemIPs == null) { return ""; } - - if (problemIPs.size() == 0) { - if (op.equals("add")) { + + if (problemIPs.size() == 0) { + if (op.equals("add")) { return "Successfully added all IPs in the specified range."; } else if (op.equals("delete")) { return "Successfully deleted all IPs in the specified range."; } else { return ""; } - } else { - String successString = ""; - if (op.equals("add")) { + } else { + String successString = ""; + if (op.equals("add")) { successString += "Failed to add the following IPs, because they are already in the database:

"; } else if (op.equals("delete")) { successString += "Failed to delete the following IPs, because they are in use:

"; } - - for (int i = 0; i < problemIPs.size(); i++) { - successString += problemIPs.elementAt(i); - if (i != (problemIPs.size() - 1)) { + + for (int i = 0; i < problemIPs.size(); i++) { + successString += problemIPs.elementAt(i); + if (i != (problemIPs.size() - 1)) { successString += ", "; } - } - - successString += "

"; - - if (op.equals("add")) { + } + + successString += "

"; + + if (op.equals("add")) { successString += "Successfully added all other IPs in the specified range."; } else if (op.equals("delete")) { successString += "Successfully deleted all other IPs in the specified range."; } - - return successString; - } - } - - public static String getCidrAddress(String pod, String zone) { - long dcId = PodZoneConfig.getZoneId(zone); - String selectSql = "SELECT * FROM `cloud`.`host_pod_ref` WHERE name = \"" + pod + "\" AND data_center_id = \"" + dcId + "\""; - String errorMsg = "Could not read CIDR address for pod/zone: " + pod + "/" + zone + " from database. Please contact Cloud Support."; - return DatabaseConfig.getDatabaseValueString(selectSql, "cidr_address", errorMsg); - } - - public static long getCidrSize(String pod, String zone) { - long dcId = PodZoneConfig.getZoneId(zone); - String selectSql = "SELECT * FROM `cloud`.`host_pod_ref` WHERE name = \"" + pod + "\" AND data_center_id = \"" + dcId + "\""; - String errorMsg = "Could not read CIDR address for pod/zone: " + pod + "/" + zone + " from database. Please contact Cloud Support."; - return DatabaseConfig.getDatabaseValueLong(selectSql, "cidr_size", errorMsg); - } - @DB - protected Vector deleteIPRange(String type, long podId, long zoneId, long vlanDbId, String startIP, String endIP) { - long startIPLong = NetUtils.ip2Long(startIP); - long endIPLong = startIPLong; - if (endIP != null) { + return successString; + } + } + + public static String getCidrAddress(String pod, String zone) { + long dcId = PodZoneConfig.getZoneId(zone); + String selectSql = "SELECT * FROM `cloud`.`host_pod_ref` WHERE name = \"" + pod + "\" AND data_center_id = \"" + dcId + "\""; + String errorMsg = "Could not read CIDR address for pod/zone: " + pod + "/" + zone + " from database. Please contact Cloud Support."; + return DatabaseConfig.getDatabaseValueString(selectSql, "cidr_address", errorMsg); + } + + public static long getCidrSize(String pod, String zone) { + long dcId = PodZoneConfig.getZoneId(zone); + String selectSql = "SELECT * FROM `cloud`.`host_pod_ref` WHERE name = \"" + pod + "\" AND data_center_id = \"" + dcId + "\""; + String errorMsg = "Could not read CIDR address for pod/zone: " + pod + "/" + zone + " from database. Please contact Cloud Support."; + return DatabaseConfig.getDatabaseValueLong(selectSql, "cidr_size", errorMsg); + } + + @DB + protected Vector deleteIPRange(String type, long podId, long zoneId, long vlanDbId, String startIP, String endIP) { + long startIPLong = NetUtils.ip2Long(startIP); + long endIPLong = startIPLong; + if (endIP != null) { endIPLong = NetUtils.ip2Long(endIP); } - - Transaction txn = Transaction.currentTxn(); - Vector problemIPs = null; - if (type.equals("public")) { + + Transaction txn = Transaction.currentTxn(); + Vector problemIPs = null; + if (type.equals("public")) { problemIPs = deletePublicIPRange(txn, startIPLong, endIPLong, vlanDbId); } else if (type.equals("private")) { problemIPs = deletePrivateIPRange(txn, startIPLong, endIPLong, podId, zoneId); } - - return problemIPs; - } - - private Vector deletePublicIPRange(Transaction txn, long startIP, long endIP, long vlanDbId) { - String deleteSql = "DELETE FROM `cloud`.`user_ip_address` WHERE public_ip_address = ? AND vlan_id = ?"; - String isPublicIPAllocatedSelectSql = "SELECT * FROM `cloud`.`user_ip_address` WHERE public_ip_address = ? AND vlan_id = ?"; - - Vector problemIPs = new Vector(); - PreparedStatement stmt = null; - PreparedStatement isAllocatedStmt = null; - - Connection conn = null; - try { - conn = txn.getConnection(); - stmt = conn.prepareStatement(deleteSql); - isAllocatedStmt = conn.prepareStatement(isPublicIPAllocatedSelectSql); - } catch (SQLException e) { - return null; - } - - while (startIP <= endIP) { - if (!isPublicIPAllocated(startIP, vlanDbId, isAllocatedStmt)) { - try { - stmt.clearParameters(); - stmt.setLong(1, startIP); - stmt.setLong(2, vlanDbId); - stmt.executeUpdate(); - } catch (Exception ex) { - } - } else { - problemIPs.add(NetUtils.long2Ip(startIP)); - } - startIP += 1; - } - - return problemIPs; - } - - private Vector deletePrivateIPRange(Transaction txn, long startIP, long endIP, long podId, long zoneId) { - String deleteSql = "DELETE FROM `cloud`.`op_dc_ip_address_alloc` WHERE ip_address = ? AND pod_id = ? AND data_center_id = ?"; - String isPrivateIPAllocatedSelectSql = "SELECT * FROM `cloud`.`op_dc_ip_address_alloc` WHERE ip_address = ? AND data_center_id = ? AND pod_id = ?"; - - Vector problemIPs = new Vector(); - PreparedStatement stmt = null; - PreparedStatement isAllocatedStmt = null; - - Connection conn = null; - try { - conn = txn.getConnection(); - stmt = conn.prepareStatement(deleteSql); - isAllocatedStmt = conn.prepareStatement(isPrivateIPAllocatedSelectSql); - } catch (SQLException e) { - System.out.println("Exception: " + e.getMessage()); - printError("Unable to start DB connection to delete private IPs. Please contact Cloud Support."); - } - - while (startIP <= endIP) { - if (!isPrivateIPAllocated(NetUtils.long2Ip(startIP), podId, zoneId, isAllocatedStmt)) { - try { - stmt.clearParameters(); - stmt.setString(1, NetUtils.long2Ip(startIP)); - stmt.setLong(2, podId); - stmt.setLong(3, zoneId); - stmt.executeUpdate(); - } catch (Exception ex) { - } - } else { - problemIPs.add(NetUtils.long2Ip(startIP)); - } - startIP += 1; - } return problemIPs; - } - - private boolean isPublicIPAllocated(long ip, long vlanDbId, PreparedStatement stmt) { - try { - stmt.clearParameters(); - stmt.setLong(1, ip); - stmt.setLong(2, vlanDbId); - ResultSet rs = stmt.executeQuery(); - if (rs.next()) { + } + + private Vector deletePublicIPRange(Transaction txn, long startIP, long endIP, long vlanDbId) { + String deleteSql = "DELETE FROM `cloud`.`user_ip_address` WHERE public_ip_address = ? AND vlan_id = ?"; + String isPublicIPAllocatedSelectSql = "SELECT * FROM `cloud`.`user_ip_address` WHERE public_ip_address = ? AND vlan_id = ?"; + + Vector problemIPs = new Vector(); + PreparedStatement stmt = null; + PreparedStatement isAllocatedStmt = null; + + Connection conn = null; + try { + conn = txn.getConnection(); + stmt = conn.prepareStatement(deleteSql); + isAllocatedStmt = conn.prepareStatement(isPublicIPAllocatedSelectSql); + } catch (SQLException e) { + return null; + } + + while (startIP <= endIP) { + if (!isPublicIPAllocated(startIP, vlanDbId, isAllocatedStmt)) { + try { + stmt.clearParameters(); + stmt.setLong(1, startIP); + stmt.setLong(2, vlanDbId); + stmt.executeUpdate(); + } catch (Exception ex) { + } + } else { + problemIPs.add(NetUtils.long2Ip(startIP)); + } + startIP += 1; + } + + return problemIPs; + } + + private Vector deletePrivateIPRange(Transaction txn, long startIP, long endIP, long podId, long zoneId) { + String deleteSql = "DELETE FROM `cloud`.`op_dc_ip_address_alloc` WHERE ip_address = ? AND pod_id = ? AND data_center_id = ?"; + String isPrivateIPAllocatedSelectSql = "SELECT * FROM `cloud`.`op_dc_ip_address_alloc` WHERE ip_address = ? AND data_center_id = ? AND pod_id = ?"; + + Vector problemIPs = new Vector(); + PreparedStatement stmt = null; + PreparedStatement isAllocatedStmt = null; + + Connection conn = null; + try { + conn = txn.getConnection(); + stmt = conn.prepareStatement(deleteSql); + isAllocatedStmt = conn.prepareStatement(isPrivateIPAllocatedSelectSql); + } catch (SQLException e) { + System.out.println("Exception: " + e.getMessage()); + printError("Unable to start DB connection to delete private IPs. Please contact Cloud Support."); + } + + while (startIP <= endIP) { + if (!isPrivateIPAllocated(NetUtils.long2Ip(startIP), podId, zoneId, isAllocatedStmt)) { + try { + stmt.clearParameters(); + stmt.setString(1, NetUtils.long2Ip(startIP)); + stmt.setLong(2, podId); + stmt.setLong(3, zoneId); + stmt.executeUpdate(); + } catch (Exception ex) { + } + } else { + problemIPs.add(NetUtils.long2Ip(startIP)); + } + startIP += 1; + } + + return problemIPs; + } + + private boolean isPublicIPAllocated(long ip, long vlanDbId, PreparedStatement stmt) { + try { + stmt.clearParameters(); + stmt.setLong(1, ip); + stmt.setLong(2, vlanDbId); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { return (rs.getString("allocated") != null); } else { return false; } } catch (SQLException ex) { - System.out.println(ex.getMessage()); + System.out.println(ex.getMessage()); return true; } - } - - private boolean isPrivateIPAllocated(String ip, long podId, long zoneId, PreparedStatement stmt) { - try { - stmt.clearParameters(); - stmt.setString(1, ip); - stmt.setLong(2, zoneId); - stmt.setLong(3, podId); - ResultSet rs = stmt.executeQuery(); - if (rs.next()) { + } + + private boolean isPrivateIPAllocated(String ip, long podId, long zoneId, PreparedStatement stmt) { + try { + stmt.clearParameters(); + stmt.setString(1, ip); + stmt.setLong(2, zoneId); + stmt.setLong(3, podId); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { return (rs.getString("taken") != null); } else { return false; } } catch (SQLException ex) { - System.out.println(ex.getMessage()); + System.out.println(ex.getMessage()); return true; } - } - - @DB - public List saveIPRange(String type, long podId, long zoneId, long vlanDbId, String startIP, String endIP, Long sourceNetworkId, long physicalNetworkId) { - long startIPLong = NetUtils.ip2Long(startIP); - long endIPLong = startIPLong; - if (endIP != null) { + } + + @DB + public List saveIPRange(String type, long podId, long zoneId, long vlanDbId, String startIP, String endIP, Long sourceNetworkId, long physicalNetworkId) { + long startIPLong = NetUtils.ip2Long(startIP); + long endIPLong = startIPLong; + if (endIP != null) { endIPLong = NetUtils.ip2Long(endIP); } - - Transaction txn = Transaction.currentTxn(); - List problemIPs = null; - - if (type.equals("public")) { + + Transaction txn = Transaction.currentTxn(); + List problemIPs = null; + + if (type.equals("public")) { problemIPs = savePublicIPRange(txn, startIPLong, endIPLong, zoneId, vlanDbId, sourceNetworkId, physicalNetworkId); } else if (type.equals("private")) { problemIPs = savePrivateIPRange(txn, startIPLong, endIPLong, podId, zoneId); } - - String[] linkLocalIps = NetUtils.getLinkLocalIPRange(10); - long startLinkLocalIp = NetUtils.ip2Long(linkLocalIps[0]); - long endLinkLocalIp = NetUtils.ip2Long(linkLocalIps[1]); - - saveLinkLocalPrivateIPRange(txn, startLinkLocalIp, endLinkLocalIp, podId, zoneId); - - return problemIPs; + + String[] linkLocalIps = NetUtils.getLinkLocalIPRange(10); + long startLinkLocalIp = NetUtils.ip2Long(linkLocalIps[0]); + long endLinkLocalIp = NetUtils.ip2Long(linkLocalIps[1]); + + saveLinkLocalPrivateIPRange(txn, startLinkLocalIp, endLinkLocalIp, podId, zoneId); + + return problemIPs; } - - public Vector savePublicIPRange(Transaction txn, long startIP, long endIP, long zoneId, long vlanDbId, Long sourceNetworkId, long physicalNetworkId) { - String insertSql = "INSERT INTO `cloud`.`user_ip_address` (public_ip_address, data_center_id, vlan_db_id, mac_address, source_network_id, physical_network_id, uuid) VALUES (?, ?, ?, (select mac_address from `cloud`.`data_center` where id=?), ?, ?, ?)"; - String updateSql = "UPDATE `cloud`.`data_center` set mac_address = mac_address+1 where id=?"; - Vector problemIPs = new Vector(); - PreparedStatement stmt = null; - - Connection conn = null; - try { - conn = txn.getConnection(); - } catch (SQLException e) { - return null; - } - + + public Vector savePublicIPRange(Transaction txn, long startIP, long endIP, long zoneId, long vlanDbId, Long sourceNetworkId, long physicalNetworkId) { + String insertSql = "INSERT INTO `cloud`.`user_ip_address` (public_ip_address, data_center_id, vlan_db_id, mac_address, source_network_id, physical_network_id, uuid) VALUES (?, ?, ?, (select mac_address from `cloud`.`data_center` where id=?), ?, ?, ?)"; + String updateSql = "UPDATE `cloud`.`data_center` set mac_address = mac_address+1 where id=?"; + Vector problemIPs = new Vector(); + PreparedStatement stmt = null; + + Connection conn = null; + try { + conn = txn.getConnection(); + } catch (SQLException e) { + return null; + } + while (startIP <= endIP) { try { - stmt = conn.prepareStatement(insertSql); - stmt.setString(1, NetUtils.long2Ip(startIP)); - stmt.setLong(2, zoneId); - stmt.setLong(3, vlanDbId); - stmt.setLong(4, zoneId); - stmt.setLong(5, sourceNetworkId); - stmt.setLong(6, physicalNetworkId); - stmt.setString(7, UUID.randomUUID().toString()); - stmt.executeUpdate(); - stmt.close(); - stmt = conn.prepareStatement(updateSql); - stmt.setLong(1, zoneId); - stmt.executeUpdate(); - stmt.close(); + stmt = conn.prepareStatement(insertSql); + stmt.setString(1, NetUtils.long2Ip(startIP)); + stmt.setLong(2, zoneId); + stmt.setLong(3, vlanDbId); + stmt.setLong(4, zoneId); + stmt.setLong(5, sourceNetworkId); + stmt.setLong(6, physicalNetworkId); + stmt.setString(7, UUID.randomUUID().toString()); + stmt.executeUpdate(); + stmt.close(); + stmt = conn.prepareStatement(updateSql); + stmt.setLong(1, zoneId); + stmt.executeUpdate(); + stmt.close(); } catch (Exception ex) { problemIPs.add(NetUtils.long2Ip(startIP)); } startIP++; } - + return problemIPs; - } - - public List savePrivateIPRange(Transaction txn, long startIP, long endIP, long podId, long zoneId) { - String insertSql = "INSERT INTO `cloud`.`op_dc_ip_address_alloc` (ip_address, data_center_id, pod_id, mac_address) VALUES (?, ?, ?, (select mac_address from `cloud`.`data_center` where id=?))"; + } + + public List savePrivateIPRange(Transaction txn, long startIP, long endIP, long podId, long zoneId) { + String insertSql = "INSERT INTO `cloud`.`op_dc_ip_address_alloc` (ip_address, data_center_id, pod_id, mac_address) VALUES (?, ?, ?, (select mac_address from `cloud`.`data_center` where id=?))"; String updateSql = "UPDATE `cloud`.`data_center` set mac_address = mac_address+1 where id=?"; - Vector problemIPs = new Vector(); - + Vector problemIPs = new Vector(); + try { Connection conn = null; conn = txn.getConnection(); while (startIP <= endIP) { try { PreparedStatement stmt = conn.prepareStatement(insertSql); - stmt.setString(1, NetUtils.long2Ip(startIP)); - stmt.setLong(2, zoneId); - stmt.setLong(3, podId); - stmt.setLong(4, zoneId); - stmt.executeUpdate(); - stmt.close(); + stmt.setString(1, NetUtils.long2Ip(startIP)); + stmt.setLong(2, zoneId); + stmt.setLong(3, podId); + stmt.setLong(4, zoneId); + stmt.executeUpdate(); + stmt.close(); stmt = conn.prepareStatement(updateSql); stmt.setLong(1, zoneId); stmt.executeUpdate(); @@ -515,30 +515,30 @@ public class IPRangeConfig { System.out.print(ex.getMessage()); ex.printStackTrace(); } - + return problemIPs; - } - - private Vector saveLinkLocalPrivateIPRange(Transaction txn, long startIP, long endIP, long podId, long zoneId) { - String insertSql = "INSERT INTO `cloud`.`op_dc_link_local_ip_address_alloc` (ip_address, data_center_id, pod_id) VALUES (?, ?, ?)"; - Vector problemIPs = new Vector(); - - Connection conn = null; - try { - conn = txn.getConnection(); - } catch (SQLException e) { - System.out.println("Exception: " + e.getMessage()); - printError("Unable to start DB connection to save private IPs. Please contact Cloud Support."); - } - + } + + private Vector saveLinkLocalPrivateIPRange(Transaction txn, long startIP, long endIP, long podId, long zoneId) { + String insertSql = "INSERT INTO `cloud`.`op_dc_link_local_ip_address_alloc` (ip_address, data_center_id, pod_id) VALUES (?, ?, ?)"; + Vector problemIPs = new Vector(); + + Connection conn = null; + try { + conn = txn.getConnection(); + } catch (SQLException e) { + System.out.println("Exception: " + e.getMessage()); + printError("Unable to start DB connection to save private IPs. Please contact Cloud Support."); + } + try { long start = startIP; PreparedStatement stmt = conn.prepareStatement(insertSql); while (startIP <= endIP) { - stmt.setString(1, NetUtils.long2Ip(startIP++)); - stmt.setLong(2, zoneId); - stmt.setLong(3, podId); - stmt.addBatch(); + stmt.setString(1, NetUtils.long2Ip(startIP++)); + stmt.setLong(2, zoneId); + stmt.setLong(3, podId); + stmt.addBatch(); } int[] results = stmt.executeBatch(); for (int i = 0; i < results.length; i += 2) { @@ -547,28 +547,28 @@ public class IPRangeConfig { } } stmt.close(); - } catch (Exception ex) { + } catch (Exception ex) { } - + return problemIPs; - } - - public static String getPublicNetmask(String zone) { - return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`data_center` WHERE name = \"" + zone + "\"", "netmask", - "Unable to start DB connection to read public netmask. Please contact Cloud Support."); - } - - public static String getPublicGateway(String zone) { - return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`data_center` WHERE name = \"" + zone + "\"", "gateway", - "Unable to start DB connection to read public gateway. Please contact Cloud Support."); - } - - public static String getGuestNetworkCidr(Long zoneId) - { - return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`data_center` WHERE id = \"" + zoneId + "\"","guest_network_cidr", - "Unable to start DB connection to read guest cidr network. Please contact Cloud Support."); - } - + } + + public static String getPublicNetmask(String zone) { + return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`data_center` WHERE name = \"" + zone + "\"", "netmask", + "Unable to start DB connection to read public netmask. Please contact Cloud Support."); + } + + public static String getPublicGateway(String zone) { + return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`data_center` WHERE name = \"" + zone + "\"", "gateway", + "Unable to start DB connection to read public gateway. Please contact Cloud Support."); + } + + public static String getGuestNetworkCidr(Long zoneId) + { + return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`data_center` WHERE id = \"" + zoneId + "\"","guest_network_cidr", + "Unable to start DB connection to read guest cidr network. Please contact Cloud Support."); + } + // public static String getGuestIpNetwork() { // return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`configuration` WHERE name = \"guest.ip.network\"", "value", // "Unable to start DB connection to read guest IP network. Please contact Cloud Support."); @@ -578,7 +578,7 @@ public class IPRangeConfig { // return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`configuration` WHERE name = \"guest.netmask\"", "value", // "Unable to start DB connection to read guest netmask. Please contact Cloud Support."); // } - + // public static String getGuestSubnet() { // String guestIpNetwork = getGuestIpNetwork(); // String guestNetmask = getGuestNetmask(); @@ -593,9 +593,9 @@ public class IPRangeConfig { // String guestNetmask = getGuestNetmask(); // return NetUtils.getCidrSize(guestNetmask); // } - - public static boolean validCIDR(final String cidr) { - if (cidr == null || cidr.isEmpty()) { + + public static boolean validCIDR(final String cidr) { + if (cidr == null || cidr.isEmpty()) { return false; } String[] cidrPair = cidr.split("\\/"); @@ -608,92 +608,92 @@ public class IPRangeConfig { return false; } int cidrSizeNum = -1; - + try { - cidrSizeNum = Integer.parseInt(cidrSize); + cidrSizeNum = Integer.parseInt(cidrSize); } catch (Exception e) { - return false; + return false; } - + if (cidrSizeNum < 1 || cidrSizeNum > 32) { return false; } - + return true; - } - - public static boolean validOrBlankIP(final String ip) { - if (ip == null || ip.isEmpty()) { + } + + public static boolean validOrBlankIP(final String ip) { + if (ip == null || ip.isEmpty()) { return true; } - return validIP(ip); - } - - public static boolean validIP(final String ip) { - final String[] ipAsList = ip.split("\\."); - - // The IP address must have four octets - if (Array.getLength(ipAsList) != 4) { - return false; - } - - for (int i = 0; i < 4; i++) { - // Each octet must be an integer - final String octetString = ipAsList[i]; - int octet; - try { - octet = Integer.parseInt(octetString); - } catch(final Exception e) { - return false; - } - // Each octet must be between 0 and 255, inclusive - if (octet < 0 || octet > 255) { + return validIP(ip); + } + + public static boolean validIP(final String ip) { + final String[] ipAsList = ip.split("\\."); + + // The IP address must have four octets + if (Array.getLength(ipAsList) != 4) { + return false; + } + + for (int i = 0; i < 4; i++) { + // Each octet must be an integer + final String octetString = ipAsList[i]; + int octet; + try { + octet = Integer.parseInt(octetString); + } catch(final Exception e) { + return false; + } + // Each octet must be between 0 and 255, inclusive + if (octet < 0 || octet > 255) { return false; } - // Each octetString must have between 1 and 3 characters - if (octetString.length() < 1 || octetString.length() > 3) { + // Each octetString must have between 1 and 3 characters + if (octetString.length() < 1 || octetString.length() > 3) { return false; } - - } - - // IP is good, return true - return true; - } - + + } + + // IP is good, return true + return true; + } + public static boolean validIPRange(String startIP, String endIP) { - if (endIP == null || endIP.isEmpty()) { + if (endIP == null || endIP.isEmpty()) { return true; } - - long startIPLong = NetUtils.ip2Long(startIP); - long endIPLong = NetUtils.ip2Long(endIP); - return (startIPLong < endIPLong); + + long startIPLong = NetUtils.ip2Long(startIP); + long endIPLong = NetUtils.ip2Long(endIP); + return (startIPLong < endIPLong); } - + public static boolean sameSubnet(final String ip1, final String ip2, final String netmask) { - if (ip1 == null || ip1.isEmpty() || ip2 == null || ip2.isEmpty()) { + if (ip1 == null || ip1.isEmpty() || ip2 == null || ip2.isEmpty()) { return true; } - String subnet1 = NetUtils.getSubNet(ip1, netmask); - String subnet2 = NetUtils.getSubNet(ip2, netmask); - - return (subnet1.equals(subnet2)); + String subnet1 = NetUtils.getSubNet(ip1, netmask); + String subnet2 = NetUtils.getSubNet(ip2, netmask); + + return (subnet1.equals(subnet2)); } - + public static boolean sameSubnetCIDR(final String ip1, final String ip2, final long cidrSize) { - if (ip1 == null || ip1.isEmpty() || ip2 == null || ip2.isEmpty()) { + if (ip1 == null || ip1.isEmpty() || ip2 == null || ip2.isEmpty()) { return true; } - String subnet1 = NetUtils.getCidrSubNet(ip1, cidrSize); - String subnet2 = NetUtils.getCidrSubNet(ip2, cidrSize); - - return (subnet1.equals(subnet2)); + String subnet1 = NetUtils.getCidrSubNet(ip1, cidrSize); + String subnet2 = NetUtils.getCidrSubNet(ip2, cidrSize); + + return (subnet1.equals(subnet2)); } - - private static void printError(String message) { - DatabaseConfig.printError(message); - } - + + private static void printError(String message) { + DatabaseConfig.printError(message); + } + } diff --git a/server/src/com/cloud/test/PodZoneConfig.java b/server/src/com/cloud/test/PodZoneConfig.java index 70ee0acc8b5..59f8b6ce12d 100644 --- a/server/src/com/cloud/test/PodZoneConfig.java +++ b/server/src/com/cloud/test/PodZoneConfig.java @@ -25,194 +25,194 @@ import java.util.List; import java.util.Vector; import com.cloud.network.Networks.TrafficType; - +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; import com.cloud.utils.net.NetUtils; public class PodZoneConfig { - - public static void main(String[] args) { - PodZoneConfig config = ComponentLocator.inject(PodZoneConfig.class); - //config.run(args); - System.exit(0); - } - - public void savePod(boolean printOutput, long id, String name, long dcId, String gateway, String cidr, int vlanStart, int vlanEnd) { - // Check that the cidr was valid - if (!IPRangeConfig.validCIDR(cidr)) printError("Please enter a valid CIDR for pod: " + name); - - // Get the individual cidrAddress and cidrSize values - String[] cidrPair = cidr.split("\\/"); - String cidrAddress = cidrPair[0]; - String cidrSize = cidrPair[1]; - String sql = null; - if (id != -1) sql = "INSERT INTO `cloud`.`host_pod_ref` (id, name, data_center_id, gateway, cidr_address, cidr_size) " + "VALUES ('" + id + "','" + name + "','" + dcId + "','" + gateway + "','" + cidrAddress + "','" + cidrSize + "')"; - else sql = "INSERT INTO `cloud`.`host_pod_ref` (name, data_center_id, gateway, cidr_address, cidr_size) " + "VALUES ('" + name + "','" + dcId + "','" + gateway + "','" + cidrAddress + "','" + cidrSize + "')"; - + public static void main(String[] args) { + PodZoneConfig config = ComponentContext.inject(PodZoneConfig.class); + //config.run(args); + System.exit(0); + } + + public void savePod(boolean printOutput, long id, String name, long dcId, String gateway, String cidr, int vlanStart, int vlanEnd) { + // Check that the cidr was valid + if (!IPRangeConfig.validCIDR(cidr)) printError("Please enter a valid CIDR for pod: " + name); + + // Get the individual cidrAddress and cidrSize values + String[] cidrPair = cidr.split("\\/"); + String cidrAddress = cidrPair[0]; + String cidrSize = cidrPair[1]; + + String sql = null; + if (id != -1) sql = "INSERT INTO `cloud`.`host_pod_ref` (id, name, data_center_id, gateway, cidr_address, cidr_size) " + "VALUES ('" + id + "','" + name + "','" + dcId + "','" + gateway + "','" + cidrAddress + "','" + cidrSize + "')"; + else sql = "INSERT INTO `cloud`.`host_pod_ref` (name, data_center_id, gateway, cidr_address, cidr_size) " + "VALUES ('" + name + "','" + dcId + "','" + gateway + "','" + cidrAddress + "','" + cidrSize + "')"; + DatabaseConfig.saveSQL(sql, "Failed to save pod due to exception. Please contact Cloud Support."); - + if (printOutput) System.out.println("Successfuly saved pod."); - } - - public void checkAllPodCidrSubnets() { - Vector allZoneIDs = getAllZoneIDs(); - for (Long dcId : allZoneIDs) { - HashMap> currentPodCidrSubnets = getCurrentPodCidrSubnets(dcId.longValue()); - String result = checkPodCidrSubnets(dcId.longValue(), currentPodCidrSubnets); - if (!result.equals("success")) printError(result); - } - } - - private String checkPodCidrSubnets(long dcId, HashMap> currentPodCidrSubnets) { - + } + + public void checkAllPodCidrSubnets() { + Vector allZoneIDs = getAllZoneIDs(); + for (Long dcId : allZoneIDs) { + HashMap> currentPodCidrSubnets = getCurrentPodCidrSubnets(dcId.longValue()); + String result = checkPodCidrSubnets(dcId.longValue(), currentPodCidrSubnets); + if (!result.equals("success")) printError(result); + } + } + + private String checkPodCidrSubnets(long dcId, HashMap> currentPodCidrSubnets) { + // DataCenterDao _dcDao = null; // final ComponentLocator locator = ComponentLocator.getLocator("management-server"); - + // _dcDao = locator.getDao(DataCenterDao.class); - // For each pod, return an error if any of the following is true: - // 1. The pod's CIDR subnet conflicts with the guest network subnet - // 2. The pod's CIDR subnet conflicts with the CIDR subnet of any other pod - - String zoneName = PodZoneConfig.getZoneName(dcId); - - //get the guest network cidr and guest netmask from the zone + // For each pod, return an error if any of the following is true: + // 1. The pod's CIDR subnet conflicts with the guest network subnet + // 2. The pod's CIDR subnet conflicts with the CIDR subnet of any other pod + + String zoneName = PodZoneConfig.getZoneName(dcId); + + //get the guest network cidr and guest netmask from the zone // DataCenterVO dcVo = _dcDao.findById(dcId); - - String guestNetworkCidr = IPRangeConfig.getGuestNetworkCidr(dcId); - - if (guestNetworkCidr == null || guestNetworkCidr.isEmpty()) return "Please specify a valid guest cidr"; + + String guestNetworkCidr = IPRangeConfig.getGuestNetworkCidr(dcId); + + if (guestNetworkCidr == null || guestNetworkCidr.isEmpty()) return "Please specify a valid guest cidr"; String[] cidrTuple = guestNetworkCidr.split("\\/"); - - String guestIpNetwork = NetUtils.getIpRangeStartIpFromCidr(cidrTuple[0], Long.parseLong(cidrTuple[1])); - long guestCidrSize = Long.parseLong(cidrTuple[1]); - + + String guestIpNetwork = NetUtils.getIpRangeStartIpFromCidr(cidrTuple[0], Long.parseLong(cidrTuple[1])); + long guestCidrSize = Long.parseLong(cidrTuple[1]); + // Iterate through all pods in this zone - for (Long podId : currentPodCidrSubnets.keySet()) { - String podName; - if (podId.longValue() == -1) podName = "newPod"; - else podName = PodZoneConfig.getPodName(podId.longValue(), dcId); - - Vector cidrPair = currentPodCidrSubnets.get(podId); - String cidrAddress = (String) cidrPair.get(0); - long cidrSize = ((Long) cidrPair.get(1)).longValue(); - - long cidrSizeToUse = -1; - if (cidrSize < guestCidrSize) cidrSizeToUse = cidrSize; - else cidrSizeToUse = guestCidrSize; - - String cidrSubnet = NetUtils.getCidrSubNet(cidrAddress, cidrSizeToUse); - String guestSubnet = NetUtils.getCidrSubNet(guestIpNetwork, cidrSizeToUse); - - // Check that cidrSubnet does not equal guestSubnet - if (cidrSubnet.equals(guestSubnet)) { - if (podName.equals("newPod")) { - return "The subnet of the pod you are adding conflicts with the subnet of the Guest IP Network. Please specify a different CIDR."; - } else { - return "Warning: The subnet of pod " + podName + " in zone " + zoneName + " conflicts with the subnet of the Guest IP Network. Please change either the pod's CIDR or the Guest IP Network's subnet, and re-run install-vmops-management."; - } - } - - // Iterate through the rest of the pods - for (Long otherPodId : currentPodCidrSubnets.keySet()) { - if (podId.equals(otherPodId)) continue; - - // Check that cidrSubnet does not equal otherCidrSubnet - Vector otherCidrPair = currentPodCidrSubnets.get(otherPodId); - String otherCidrAddress = (String) otherCidrPair.get(0); - long otherCidrSize = ((Long) otherCidrPair.get(1)).longValue(); - - if (cidrSize < otherCidrSize) cidrSizeToUse = cidrSize; - else cidrSizeToUse = otherCidrSize; - - cidrSubnet = NetUtils.getCidrSubNet(cidrAddress, cidrSizeToUse); - String otherCidrSubnet = NetUtils.getCidrSubNet(otherCidrAddress, cidrSizeToUse); - - if (cidrSubnet.equals(otherCidrSubnet)) { - String otherPodName = PodZoneConfig.getPodName(otherPodId.longValue(), dcId); - if (podName.equals("newPod")) { - return "The subnet of the pod you are adding conflicts with the subnet of pod " + otherPodName + " in zone " + zoneName + ". Please specify a different CIDR."; - } else { - return "Warning: The pods " + podName + " and " + otherPodName + " in zone " + zoneName + " have conflicting CIDR subnets. Please change the CIDR of one of these pods."; - } - } - } - } - - return "success"; - } - - @DB - protected HashMap> getCurrentPodCidrSubnets(long dcId) { - HashMap> currentPodCidrSubnets = new HashMap>(); - - String selectSql = "SELECT id, cidr_address, cidr_size FROM host_pod_ref WHERE data_center_id=" + dcId; - Transaction txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(selectSql); - ResultSet rs = stmt.executeQuery(); - while (rs.next()) { - Long podId = rs.getLong("id"); - String cidrAddress = rs.getString("cidr_address"); - long cidrSize = rs.getLong("cidr_size"); - Vector cidrPair = new Vector(); - cidrPair.add(0, cidrAddress); - cidrPair.add(1, new Long(cidrSize)); - currentPodCidrSubnets.put(podId, cidrPair); - } + for (Long podId : currentPodCidrSubnets.keySet()) { + String podName; + if (podId.longValue() == -1) podName = "newPod"; + else podName = PodZoneConfig.getPodName(podId.longValue(), dcId); + + Vector cidrPair = currentPodCidrSubnets.get(podId); + String cidrAddress = (String) cidrPair.get(0); + long cidrSize = ((Long) cidrPair.get(1)).longValue(); + + long cidrSizeToUse = -1; + if (cidrSize < guestCidrSize) cidrSizeToUse = cidrSize; + else cidrSizeToUse = guestCidrSize; + + String cidrSubnet = NetUtils.getCidrSubNet(cidrAddress, cidrSizeToUse); + String guestSubnet = NetUtils.getCidrSubNet(guestIpNetwork, cidrSizeToUse); + + // Check that cidrSubnet does not equal guestSubnet + if (cidrSubnet.equals(guestSubnet)) { + if (podName.equals("newPod")) { + return "The subnet of the pod you are adding conflicts with the subnet of the Guest IP Network. Please specify a different CIDR."; + } else { + return "Warning: The subnet of pod " + podName + " in zone " + zoneName + " conflicts with the subnet of the Guest IP Network. Please change either the pod's CIDR or the Guest IP Network's subnet, and re-run install-vmops-management."; + } + } + + // Iterate through the rest of the pods + for (Long otherPodId : currentPodCidrSubnets.keySet()) { + if (podId.equals(otherPodId)) continue; + + // Check that cidrSubnet does not equal otherCidrSubnet + Vector otherCidrPair = currentPodCidrSubnets.get(otherPodId); + String otherCidrAddress = (String) otherCidrPair.get(0); + long otherCidrSize = ((Long) otherCidrPair.get(1)).longValue(); + + if (cidrSize < otherCidrSize) cidrSizeToUse = cidrSize; + else cidrSizeToUse = otherCidrSize; + + cidrSubnet = NetUtils.getCidrSubNet(cidrAddress, cidrSizeToUse); + String otherCidrSubnet = NetUtils.getCidrSubNet(otherCidrAddress, cidrSizeToUse); + + if (cidrSubnet.equals(otherCidrSubnet)) { + String otherPodName = PodZoneConfig.getPodName(otherPodId.longValue(), dcId); + if (podName.equals("newPod")) { + return "The subnet of the pod you are adding conflicts with the subnet of pod " + otherPodName + " in zone " + zoneName + ". Please specify a different CIDR."; + } else { + return "Warning: The pods " + podName + " and " + otherPodName + " in zone " + zoneName + " have conflicting CIDR subnets. Please change the CIDR of one of these pods."; + } + } + } + } + + return "success"; + } + + @DB + protected HashMap> getCurrentPodCidrSubnets(long dcId) { + HashMap> currentPodCidrSubnets = new HashMap>(); + + String selectSql = "SELECT id, cidr_address, cidr_size FROM host_pod_ref WHERE data_center_id=" + dcId; + Transaction txn = Transaction.currentTxn(); + try { + PreparedStatement stmt = txn.prepareAutoCloseStatement(selectSql); + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + Long podId = rs.getLong("id"); + String cidrAddress = rs.getString("cidr_address"); + long cidrSize = rs.getLong("cidr_size"); + Vector cidrPair = new Vector(); + cidrPair.add(0, cidrAddress); + cidrPair.add(1, new Long(cidrSize)); + currentPodCidrSubnets.put(podId, cidrPair); + } } catch (SQLException ex) { - System.out.println(ex.getMessage()); - printError("There was an issue with reading currently saved pod CIDR subnets. Please contact Cloud Support."); + System.out.println(ex.getMessage()); + printError("There was an issue with reading currently saved pod CIDR subnets. Please contact Cloud Support."); return null; } - + return currentPodCidrSubnets; - } - - public void deletePod(String name, long dcId) { - String sql = "DELETE FROM `cloud`.`host_pod_ref` WHERE name=\"" + name + "\" AND data_center_id=\"" + dcId + "\""; - DatabaseConfig.saveSQL(sql, "Failed to delete pod due to exception. Please contact Cloud Support."); - } - - public long getVlanDbId(String zone, String vlanId) { - long zoneId = getZoneId(zone); - - return DatabaseConfig.getDatabaseValueLong("SELECT * FROM `cloud`.`vlan` WHERE data_center_id=\"" + zoneId + "\" AND vlan_id =\"" + vlanId + "\"", "id", - "Unable to start DB connection to read vlan DB id. Please contact Cloud Support."); } - - public List modifyVlan(String zone, boolean add, String vlanId, String vlanGateway, String vlanNetmask, String pod, String vlanType, String ipRange, long networkId, long physicalNetworkId) { - // Check if the zone is valid - long zoneId = getZoneId(zone); - if (zoneId == -1) - return genReturnList("false", "Please specify a valid zone."); - - //check if physical network is valid + + public void deletePod(String name, long dcId) { + String sql = "DELETE FROM `cloud`.`host_pod_ref` WHERE name=\"" + name + "\" AND data_center_id=\"" + dcId + "\""; + DatabaseConfig.saveSQL(sql, "Failed to delete pod due to exception. Please contact Cloud Support."); + } + + public long getVlanDbId(String zone, String vlanId) { + long zoneId = getZoneId(zone); + + return DatabaseConfig.getDatabaseValueLong("SELECT * FROM `cloud`.`vlan` WHERE data_center_id=\"" + zoneId + "\" AND vlan_id =\"" + vlanId + "\"", "id", + "Unable to start DB connection to read vlan DB id. Please contact Cloud Support."); + } + + public List modifyVlan(String zone, boolean add, String vlanId, String vlanGateway, String vlanNetmask, String pod, String vlanType, String ipRange, long networkId, long physicalNetworkId) { + // Check if the zone is valid + long zoneId = getZoneId(zone); + if (zoneId == -1) + return genReturnList("false", "Please specify a valid zone."); + + //check if physical network is valid long physicalNetworkDbId = checkPhysicalNetwork(physicalNetworkId); if (physicalNetworkId == -1) return genReturnList("false", "Please specify a valid physical network."); - - - Long podId = pod!=null?getPodId(pod, zone):null; - if (podId != null && podId == -1) - return genReturnList("false", "Please specify a valid pod."); - - if (add) { - - // Make sure the gateway is valid - if (!NetUtils.isValidIp(vlanGateway)) - return genReturnList("false", "Please specify a valid gateway."); - - // Make sure the netmask is valid - if (!NetUtils.isValidIp(vlanNetmask)) - return genReturnList("false", "Please specify a valid netmask."); - - // Check if a vlan with the same vlanId already exists in the specified zone - if (getVlanDbId(zone, vlanId) != -1) - return genReturnList("false", "A VLAN with the specified VLAN ID already exists in zone " + zone + "."); - - /* + + + Long podId = pod!=null?getPodId(pod, zone):null; + if (podId != null && podId == -1) + return genReturnList("false", "Please specify a valid pod."); + + if (add) { + + // Make sure the gateway is valid + if (!NetUtils.isValidIp(vlanGateway)) + return genReturnList("false", "Please specify a valid gateway."); + + // Make sure the netmask is valid + if (!NetUtils.isValidIp(vlanNetmask)) + return genReturnList("false", "Please specify a valid netmask."); + + // Check if a vlan with the same vlanId already exists in the specified zone + if (getVlanDbId(zone, vlanId) != -1) + return genReturnList("false", "A VLAN with the specified VLAN ID already exists in zone " + zone + "."); + + /* // Check if another vlan in the same zone has the same subnet String newVlanSubnet = NetUtils.getSubNet(vlanGateway, vlanNetmask); List vlans = _vlanDao.findByZone(zoneId); @@ -221,146 +221,146 @@ public class PodZoneConfig { if (newVlanSubnet.equals(currentVlanSubnet)) return genReturnList("false", "The VLAN with ID " + vlan.getVlanId() + " in zone " + zone + " has the same subnet. Please specify a different gateway/netmask."); } - */ - - // Everything was fine, so persist the VLAN - saveVlan(zoneId, podId, vlanId, vlanGateway, vlanNetmask, vlanType, ipRange, networkId, physicalNetworkDbId); + */ + + // Everything was fine, so persist the VLAN + saveVlan(zoneId, podId, vlanId, vlanGateway, vlanNetmask, vlanType, ipRange, networkId, physicalNetworkDbId); if (podId != null) { - long vlanDbId = getVlanDbId(zone, vlanId); - String sql = "INSERT INTO `cloud`.`pod_vlan_map` (pod_id, vlan_db_id) " + "VALUES ('" + podId + "','" + vlanDbId + "')"; + long vlanDbId = getVlanDbId(zone, vlanId); + String sql = "INSERT INTO `cloud`.`pod_vlan_map` (pod_id, vlan_db_id) " + "VALUES ('" + podId + "','" + vlanDbId + "')"; DatabaseConfig.saveSQL(sql, "Failed to save pod_vlan_map due to exception vlanDbId=" + vlanDbId + ", podId=" + podId + ". Please contact Cloud Support."); } - - return genReturnList("true", "Successfully added VLAN."); - - } else { - return genReturnList("false", "That operation is not suppored."); - } - - /* + + return genReturnList("true", "Successfully added VLAN."); + + } else { + return genReturnList("false", "That operation is not suppored."); + } + + /* else { - + // Check if a VLAN actually exists in the specified zone long vlanDbId = getVlanDbId(zone, vlanId); if (vlanDbId == -1) return genReturnList("false", "A VLAN with ID " + vlanId + " does not exist in zone " + zone); - + // Check if there are any public IPs that are in the specified vlan. List ips = _publicIpAddressDao.listByVlanDbId(vlanDbId); if (ips.size() != 0) return genReturnList("false", "Please delete all IP addresses that are in VLAN " + vlanId + " before deleting the VLAN."); - + // Delete the vlan _vlanDao.delete(vlanDbId); - + return genReturnList("true", "Successfully deleted VLAN."); } - */ + */ } - - @DB - public void saveZone(boolean printOutput, long id, String name, String dns1, String dns2, String dns3, String dns4, String guestNetworkCidr, String networkType) { - - if (printOutput) System.out.println("Saving zone, please wait..."); - - String columns = null; - String values = null; - - if (id != -1) { - columns = "(id, name"; - values = "('" + id + "','" + name + "'"; - } else { - columns = "(name"; - values = "('" + name + "'"; - } - if (dns1 != null) { - columns += ", dns1"; - values += ",'" + dns1 + "'"; - } - - if (dns2 != null) { - columns += ", dns2"; - values += ",'" + dns2 + "'"; - } - - if (dns3 != null) { - columns += ", internal_dns1"; - values += ",'" + dns3 + "'"; - } - - if (dns4 != null) { - columns += ", internal_dns2"; - values += ",'" + dns4 + "'"; - } - - if(guestNetworkCidr != null) { - columns += ", guest_network_cidr"; - values += ",'" + guestNetworkCidr + "'"; - } - - if(networkType != null) { - columns += ", networktype"; - values += ",'" + networkType + "'"; - } - - columns += ", uuid"; - values += ", UUID()"; - - columns += ")"; - values += ")"; - - String sql = "INSERT INTO `cloud`.`data_center` " + columns + " VALUES " + values; - - DatabaseConfig.saveSQL(sql, "Failed to save zone due to exception. Please contact Cloud Support."); - - if (printOutput) System.out.println("Successfully saved zone."); - } - - @DB - public void savePhysicalNetwork(boolean printOutput, long id, long dcId, int vnetStart, int vnetEnd) { - - if (printOutput) System.out.println("Saving physical network, please wait..."); - + @DB + public void saveZone(boolean printOutput, long id, String name, String dns1, String dns2, String dns3, String dns4, String guestNetworkCidr, String networkType) { + + if (printOutput) System.out.println("Saving zone, please wait..."); + String columns = null; String values = null; - + + if (id != -1) { + columns = "(id, name"; + values = "('" + id + "','" + name + "'"; + } else { + columns = "(name"; + values = "('" + name + "'"; + } + + if (dns1 != null) { + columns += ", dns1"; + values += ",'" + dns1 + "'"; + } + + if (dns2 != null) { + columns += ", dns2"; + values += ",'" + dns2 + "'"; + } + + if (dns3 != null) { + columns += ", internal_dns1"; + values += ",'" + dns3 + "'"; + } + + if (dns4 != null) { + columns += ", internal_dns2"; + values += ",'" + dns4 + "'"; + } + + if(guestNetworkCidr != null) { + columns += ", guest_network_cidr"; + values += ",'" + guestNetworkCidr + "'"; + } + + if(networkType != null) { + columns += ", networktype"; + values += ",'" + networkType + "'"; + } + + columns += ", uuid"; + values += ", UUID()"; + + columns += ")"; + values += ")"; + + String sql = "INSERT INTO `cloud`.`data_center` " + columns + " VALUES " + values; + + DatabaseConfig.saveSQL(sql, "Failed to save zone due to exception. Please contact Cloud Support."); + + if (printOutput) System.out.println("Successfully saved zone."); + } + + @DB + public void savePhysicalNetwork(boolean printOutput, long id, long dcId, int vnetStart, int vnetEnd) { + + if (printOutput) System.out.println("Saving physical network, please wait..."); + + String columns = null; + String values = null; + columns = "(id "; values = "('" + id + "'"; - + columns += ", name "; values += ",'physical network'"; - + columns += ", data_center_id "; values += ",'" + dcId + "'"; - + //save vnet information columns += ", vnet"; values += ",'" + vnetStart + "-" + vnetEnd + "'"; - + columns += ", state"; values += ", 'Enabled'"; - + columns += ", uuid"; values += ", UUID()"; - + columns += ")"; values += ")"; - + String sql = "INSERT INTO `cloud`.`physical_network` " + columns + " VALUES " + values; - + DatabaseConfig.saveSQL(sql, "Failed to save physical network due to exception. Please contact Cloud Support."); - + // Hardcode the vnet range to be the full range int begin = 0x64; int end = 64000; - + // If vnet arguments were passed in, use them if (vnetStart != -1 && vnetEnd != -1) { begin = vnetStart; end = vnetEnd; } - + String insertVnet = "INSERT INTO `cloud`.`op_dc_vnet_alloc` (vnet, data_center_id, physical_network_id) VALUES ( ?, ?, ?)"; Transaction txn = Transaction.currentTxn(); @@ -376,17 +376,17 @@ public class PodZoneConfig { } catch (SQLException ex) { printError("Error creating vnet for the physical network. Please contact Cloud Support."); } - + //add default traffic types - + //get default Xen network labels String defaultXenPrivateNetworkLabel = getDefaultXenNetworkLabel(TrafficType.Management); String defaultXenPublicNetworkLabel = getDefaultXenNetworkLabel(TrafficType.Public); String defaultXenStorageNetworkLabel = getDefaultXenNetworkLabel(TrafficType.Storage); String defaultXenGuestNetworkLabel = getDefaultXenNetworkLabel(TrafficType.Guest); - + String insertTraficType = "INSERT INTO `cloud`.`physical_network_traffic_types` " + - "(physical_network_id, traffic_type, xen_network_label) VALUES ( ?, ?, ?)"; + "(physical_network_id, traffic_type, xen_network_label) VALUES ( ?, ?, ?)"; try { PreparedStatement stmt = txn.prepareAutoCloseStatement(insertTraficType); @@ -406,128 +406,128 @@ public class PodZoneConfig { }else if(traffic.equals(TrafficType.Guest)){ stmt.setString(3, defaultXenGuestNetworkLabel); } - + stmt.addBatch(); } stmt.executeBatch(); } catch (SQLException ex) { printError("Error adding default traffic types for the physical network. Please contact Cloud Support."); } - + if (printOutput) System.out.println("Successfully saved physical network."); } - + private String getDefaultXenNetworkLabel(TrafficType trafficType){ String xenLabel = null; String configName = null; switch(trafficType){ - case Public: configName = "xen.public.network.device"; - break; - case Guest: configName = "xen.guest.network.device"; - break; - case Storage: configName = "xen.storage.network.device1"; - break; - case Management: configName = "xen.private.network.device"; - break; + case Public: configName = "xen.public.network.device"; + break; + case Guest: configName = "xen.guest.network.device"; + break; + case Storage: configName = "xen.storage.network.device1"; + break; + case Management: configName = "xen.private.network.device"; + break; } - + if(configName != null){ xenLabel = getConfiguredValue(configName); } return xenLabel; } - + public static String getConfiguredValue(String configName) { return DatabaseConfig.getDatabaseValueString("SELECT value FROM `cloud`.`configuration` where name = \"" + configName + "\"","value", "Unable to start DB connection to read configuration. Please contact Cloud Support."); } - - public void deleteZone(String name) { - String sql = "DELETE FROM `cloud`.`data_center` WHERE name=\"" + name + "\""; - DatabaseConfig.saveSQL(sql, "Failed to delete zone due to exception. Please contact Cloud Support."); - } - - public void saveVlan(long zoneId, Long podId, String vlanId, String vlanGateway, String vlanNetmask, String vlanType, String ipRange, long networkId, long physicalNetworkId) { - String sql = "INSERT INTO `cloud`.`vlan` (vlan_id, vlan_gateway, vlan_netmask, data_center_id, vlan_type, description, network_id, physical_network_id) " + "VALUES ('" + vlanId + "','" + vlanGateway + "','" + vlanNetmask + "','" + zoneId + "','" + vlanType + "','" + ipRange + "','" + networkId + "','" + physicalNetworkId + "')"; + + public void deleteZone(String name) { + String sql = "DELETE FROM `cloud`.`data_center` WHERE name=\"" + name + "\""; + DatabaseConfig.saveSQL(sql, "Failed to delete zone due to exception. Please contact Cloud Support."); + } + + public void saveVlan(long zoneId, Long podId, String vlanId, String vlanGateway, String vlanNetmask, String vlanType, String ipRange, long networkId, long physicalNetworkId) { + String sql = "INSERT INTO `cloud`.`vlan` (vlan_id, vlan_gateway, vlan_netmask, data_center_id, vlan_type, description, network_id, physical_network_id) " + "VALUES ('" + vlanId + "','" + vlanGateway + "','" + vlanNetmask + "','" + zoneId + "','" + vlanType + "','" + ipRange + "','" + networkId + "','" + physicalNetworkId + "')"; DatabaseConfig.saveSQL(sql, "Failed to save vlan due to exception. Please contact Cloud Support."); - } - - public static long getPodId(String pod, String zone) { - long dcId = getZoneId(zone); - String selectSql = "SELECT * FROM `cloud`.`host_pod_ref` WHERE name = \"" + pod + "\" AND data_center_id = \"" + dcId + "\""; - String errorMsg = "Could not read pod ID fro mdatabase. Please contact Cloud Support."; - return DatabaseConfig.getDatabaseValueLong(selectSql, "id", errorMsg); - } - - public static long getPodId(String pod, long dcId) { + } + + public static long getPodId(String pod, String zone) { + long dcId = getZoneId(zone); String selectSql = "SELECT * FROM `cloud`.`host_pod_ref` WHERE name = \"" + pod + "\" AND data_center_id = \"" + dcId + "\""; String errorMsg = "Could not read pod ID fro mdatabase. Please contact Cloud Support."; return DatabaseConfig.getDatabaseValueLong(selectSql, "id", errorMsg); } - - public static long getZoneId(String zone) { - String selectSql = "SELECT * FROM `cloud`.`data_center` WHERE name = \"" + zone + "\""; - String errorMsg = "Could not read zone ID from database. Please contact Cloud Support."; - return DatabaseConfig.getDatabaseValueLong(selectSql, "id", errorMsg); - } - + + public static long getPodId(String pod, long dcId) { + String selectSql = "SELECT * FROM `cloud`.`host_pod_ref` WHERE name = \"" + pod + "\" AND data_center_id = \"" + dcId + "\""; + String errorMsg = "Could not read pod ID fro mdatabase. Please contact Cloud Support."; + return DatabaseConfig.getDatabaseValueLong(selectSql, "id", errorMsg); + } + + public static long getZoneId(String zone) { + String selectSql = "SELECT * FROM `cloud`.`data_center` WHERE name = \"" + zone + "\""; + String errorMsg = "Could not read zone ID from database. Please contact Cloud Support."; + return DatabaseConfig.getDatabaseValueLong(selectSql, "id", errorMsg); + } + public static long checkPhysicalNetwork(long physicalNetworkId) { String selectSql = "SELECT * FROM `cloud`.`physical_network` WHERE id = \"" + physicalNetworkId + "\""; String errorMsg = "Could not read physicalNetwork ID from database. Please contact Cloud Support."; return DatabaseConfig.getDatabaseValueLong(selectSql, "id", errorMsg); } - - @DB - public Vector getAllZoneIDs() { - Vector allZoneIDs = new Vector(); - - String selectSql = "SELECT id FROM data_center"; - Transaction txn = Transaction.currentTxn(); - try { - PreparedStatement stmt = txn.prepareAutoCloseStatement(selectSql); - ResultSet rs = stmt.executeQuery(); - while (rs.next()) { - Long dcId = rs.getLong("id"); - allZoneIDs.add(dcId); - } + + @DB + public Vector getAllZoneIDs() { + Vector allZoneIDs = new Vector(); + + String selectSql = "SELECT id FROM data_center"; + Transaction txn = Transaction.currentTxn(); + try { + PreparedStatement stmt = txn.prepareAutoCloseStatement(selectSql); + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + Long dcId = rs.getLong("id"); + allZoneIDs.add(dcId); + } } catch (SQLException ex) { - System.out.println(ex.getMessage()); - printError("There was an issue with reading zone IDs. Please contact Cloud Support."); + System.out.println(ex.getMessage()); + printError("There was an issue with reading zone IDs. Please contact Cloud Support."); return null; } - + return allZoneIDs; - } - - - public static boolean validPod(String pod, String zone) { - return (getPodId(pod, zone) != -1); - } - - public static boolean validZone(String zone) { - return (getZoneId(zone) != -1); - } - - public static String getPodName(long podId, long dcId) { - return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`host_pod_ref` WHERE id=" + podId + " AND data_center_id=" + dcId, "name", - "Unable to start DB connection to read pod name. Please contact Cloud Support."); - } - - public static String getZoneName(long dcId) { - return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`data_center` WHERE id=" + dcId, "name", - "Unable to start DB connection to read zone name. Please contact Cloud Support."); - } - - private static void printError(String message) { - DatabaseConfig.printError(message); - } - - private List genReturnList(String success, String message) { - List returnList = new ArrayList(); - returnList.add(0, success); - returnList.add(1, message); - return returnList; } - + + + public static boolean validPod(String pod, String zone) { + return (getPodId(pod, zone) != -1); + } + + public static boolean validZone(String zone) { + return (getZoneId(zone) != -1); + } + + public static String getPodName(long podId, long dcId) { + return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`host_pod_ref` WHERE id=" + podId + " AND data_center_id=" + dcId, "name", + "Unable to start DB connection to read pod name. Please contact Cloud Support."); + } + + public static String getZoneName(long dcId) { + return DatabaseConfig.getDatabaseValueString("SELECT * FROM `cloud`.`data_center` WHERE id=" + dcId, "name", + "Unable to start DB connection to read zone name. Please contact Cloud Support."); + } + + private static void printError(String message) { + DatabaseConfig.printError(message); + } + + private List genReturnList(String success, String message) { + List returnList = new ArrayList(); + returnList.add(0, success); + returnList.add(1, message); + return returnList; + } + } diff --git a/server/src/com/cloud/vm/SystemVmLoadScanner.java b/server/src/com/cloud/vm/SystemVmLoadScanner.java index 174d8c7a3e6..4251b405e1b 100644 --- a/server/src/com/cloud/vm/SystemVmLoadScanner.java +++ b/server/src/com/cloud/vm/SystemVmLoadScanner.java @@ -22,7 +22,6 @@ import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; -import com.cloud.cluster.StackMaid; import com.cloud.utils.Pair; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.GlobalLock; @@ -32,27 +31,27 @@ import com.cloud.utils.db.Transaction; // TODO: simple load scanner, to minimize code changes required in console proxy manager and SSVM, we still leave most of work at handler // public class SystemVmLoadScanner { - public enum AfterScanAction { nop, expand, shrink } - + public enum AfterScanAction { nop, expand, shrink } + private static final Logger s_logger = Logger.getLogger(SystemVmLoadScanner.class); private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 3; // 3 seconds - - private final SystemVmLoadScanHandler _scanHandler; + + private final SystemVmLoadScanHandler _scanHandler; private final ScheduledExecutorService _capacityScanScheduler; private final GlobalLock _capacityScanLock; - - public SystemVmLoadScanner(SystemVmLoadScanHandler scanHandler) { - _scanHandler = scanHandler; - _capacityScanScheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory(scanHandler.getScanHandlerName())); - _capacityScanLock = GlobalLock.getInternLock(scanHandler.getScanHandlerName() + ".scan.lock"); - } - - public void initScan(long startupDelayMs, long scanIntervalMs) { + + public SystemVmLoadScanner(SystemVmLoadScanHandler scanHandler) { + _scanHandler = scanHandler; + _capacityScanScheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory(scanHandler.getScanHandlerName())); + _capacityScanLock = GlobalLock.getInternLock(scanHandler.getScanHandlerName() + ".scan.lock"); + } + + public void initScan(long startupDelayMs, long scanIntervalMs) { _capacityScanScheduler.scheduleAtFixedRate(getCapacityScanTask(), startupDelayMs, scanIntervalMs, TimeUnit.MILLISECONDS); - } - - public void stop() { + } + + public void stop() { _capacityScanScheduler.shutdownNow(); try { @@ -61,8 +60,8 @@ public class SystemVmLoadScanner { } _capacityScanLock.releaseRef(); - } - + } + private Runnable getCapacityScanTask() { return new Runnable() { @@ -74,56 +73,55 @@ public class SystemVmLoadScanner { } catch (Throwable e) { s_logger.warn("Unexpected exception " + e.getMessage(), e); } finally { - StackMaid.current().exitCleanup(); txn.close(); } } private void reallyRun() { - loadScan(); + loadScan(); } }; } - + private void loadScan() { - if(!_scanHandler.canScan()) { - return; - } - + if(!_scanHandler.canScan()) { + return; + } + if (!_capacityScanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) { if (s_logger.isTraceEnabled()) { s_logger.trace("Capacity scan lock is used by others, skip and wait for my turn"); } return; } - + try { - _scanHandler.onScanStart(); - - T[] pools = _scanHandler.getScannablePools(); - for(T p : pools) { - if(_scanHandler.isPoolReadyForScan(p)) { - Pair actionInfo = _scanHandler.scanPool(p); - - switch(actionInfo.first()) { - case nop: - break; - - case expand: - _scanHandler.expandPool(p, actionInfo.second()); - break; - - case shrink: - _scanHandler.shrinkPool(p, actionInfo.second()); - break; - } - } - } - - _scanHandler.onScanEnd(); - + _scanHandler.onScanStart(); + + T[] pools = _scanHandler.getScannablePools(); + for(T p : pools) { + if(_scanHandler.isPoolReadyForScan(p)) { + Pair actionInfo = _scanHandler.scanPool(p); + + switch(actionInfo.first()) { + case nop: + break; + + case expand: + _scanHandler.expandPool(p, actionInfo.second()); + break; + + case shrink: + _scanHandler.shrinkPool(p, actionInfo.second()); + break; + } + } + } + + _scanHandler.onScanEnd(); + } finally { - _capacityScanLock.unlock(); + _capacityScanLock.unlock(); } } } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 197230475b8..ac14e77c510 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -214,7 +214,6 @@ import com.cloud.uservm.UserVm; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.PasswordGenerator; - import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.crypt.RSAHelper; @@ -381,7 +380,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager protected String _instance; protected String _zone; - private ConfigurationDao _configDao; + @Inject ConfigurationDao _configDao; private int _createprivatetemplatefromvolumewait; private int _createprivatetemplatefromsnapshotwait; @@ -1333,8 +1332,6 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager throws ConfigurationException { _name = name; - ComponentLocator locator = ComponentLocator.getCurrentLocator(); - _configDao = locator.getDao(ConfigurationDao.class); if (_configDao == null) { throw new ConfigurationException( "Unable to get the configuration dao."); diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index 2897a63afed..95dc9a66458 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -71,7 +71,6 @@ import com.cloud.agent.manager.allocator.HostAllocator; import com.cloud.alert.AlertManager; import com.cloud.capacity.CapacityManager; import com.cloud.cluster.ClusterManager; -import com.cloud.cluster.StackMaid; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.dao.ConfigurationDao; @@ -238,7 +237,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene @Inject protected ConfigurationDao _configDao; - + Map> _vmGurus = new HashMap>(); protected StateMachine2 _stateMachine; @@ -288,7 +287,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene if (s_logger.isDebugEnabled()) { s_logger.debug("Allocating nics for " + vm); } - + try { _networkMgr.allocate(vmProfile, networks); } catch (ConcurrentOperationException e) { @@ -673,7 +672,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } continue; } - + StoragePoolVO pool = _storagePoolDao.findById(vol.getPoolId()); if (!pool.isInMaintenance()) { if (s_logger.isDebugEnabled()) { @@ -707,7 +706,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } } } - + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, account, params); DeployDestination dest = null; for (DeploymentPlanner planner : _planners) { @@ -757,7 +756,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene if(!reuseVolume){ reuseVolume = true; } - + Commands cmds = null; vmGuru.finalizeVirtualMachineProfile(vmProfile, dest, ctx); @@ -776,10 +775,10 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene _workDao.updateStep(work, Step.Starting); _agentMgr.send(destHostId, cmds); - + _workDao.updateStep(work, Step.Started); - - + + StartAnswer startAnswer = cmds.getAnswer(StartAnswer.class); if (startAnswer != null && startAnswer.getResult()) { String host_guid = startAnswer.getHost_guid(); @@ -803,7 +802,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene if (s_logger.isDebugEnabled()) { s_logger.info("The guru did not like the answers so stopping " + vm); } - + StopCommand cmd = new StopCommand(vm.getInstanceName()); StopAnswer answer = (StopAnswer) _agentMgr.easySend(destHostId, cmd); if (answer == null || !answer.getResult()) { @@ -815,7 +814,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } } s_logger.info("Unable to start VM on " + dest.getHost() + " due to " + (startAnswer == null ? " no start answer" : startAnswer.getDetails())); - + } catch (OperationTimedoutException e) { s_logger.debug("Unable to send the start command to host " + dest.getHost()); if (e.isActive()) { @@ -1071,7 +1070,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } vmGuru.prepareStop(profile); - + StopCommand stop = new StopCommand(vm, vm.getInstanceName(), null); boolean stopped = false; StopAnswer answer = null; @@ -1560,13 +1559,13 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene public boolean isVirtualMachineUpgradable(VirtualMachine vm, ServiceOffering offering) { boolean isMachineUpgradable = true; for(HostAllocator allocator : _hostAllocators) { - isMachineUpgradable = allocator.isVirtualMachineUpgradable(vm, offering); - if(isMachineUpgradable) - continue; - else - break; + isMachineUpgradable = allocator.isVirtualMachineUpgradable(vm, offering); + if(isMachineUpgradable) + continue; + else + break; } - + return isMachineUpgradable; } @@ -1644,7 +1643,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene commands.addCommand(command); } } - + for (final AgentVmInfo left : infos.values()) { boolean found = false; for (VirtualMachineGuru vmGuru : _vmGurus.values()) { @@ -1740,7 +1739,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene public void fullSync(final long clusterId, Map> newStates) { - if (newStates==null)return; + if (newStates==null)return; Map infos = convertToInfos(newStates); Set set_vms = Collections.synchronizedSet(new HashSet()); set_vms.addAll(_vmDao.listByClusterId(clusterId)); @@ -1750,11 +1749,11 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene AgentVmInfo info = infos.remove(vm.getId()); VMInstanceVO castedVm = null; if ((info == null && (vm.getState() == State.Running || vm.getState() == State.Starting)) - || (info != null && (info.state == State.Running && vm.getState() == State.Starting))) + || (info != null && (info.state == State.Running && vm.getState() == State.Starting))) { - s_logger.info("Found vm " + vm.getInstanceName() + " in inconsistent state. " + vm.getState() + " on CS while " + (info == null ? "Stopped" : "Running") + " on agent"); + s_logger.info("Found vm " + vm.getInstanceName() + " in inconsistent state. " + vm.getState() + " on CS while " + (info == null ? "Stopped" : "Running") + " on agent"); info = new AgentVmInfo(vm.getInstanceName(), getVmGuru(vm), vm, State.Stopped); - + // Bug 13850- grab outstanding work item if any for this VM state so that we mark it as DONE after we change VM state, else it will remain pending ItWorkVO work = _workDao.findByOutstandingWork(vm.getId(), vm.getState()); if (work != null) { @@ -1763,8 +1762,8 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } } vm.setState(State.Running); // set it as running and let HA take care of it - _vmDao.persist(vm); - + _vmDao.persist(vm); + if (work != null) { if (s_logger.isDebugEnabled()) { s_logger.debug("Updating outstanding work item to Done, id:" + work.getId()); @@ -1772,7 +1771,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene work.setStep(Step.Done); _workDao.update(work.getId(), work); } - + castedVm = info.guru.findById(vm.getId()); try { Host host = _hostDao.findByGuid(info.getHostUuid()); @@ -1812,20 +1811,20 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } } else - // host id can change - if (info != null && vm.getState() == State.Running){ - // check for host id changes - Host host = _hostDao.findByGuid(info.getHostUuid()); - if (host != null && (vm.getHostId() == null || host.getId() != vm.getHostId())){ - s_logger.info("Found vm " + vm.getInstanceName() + " with inconsistent host in db, new host is " + host.getId()); - try { - stateTransitTo(vm, VirtualMachine.Event.AgentReportMigrated, host.getId()); - } catch (NoTransitionException e) { - s_logger.warn(e.getMessage()); - } - } - } - /* else if(info == null && vm.getState() == State.Stopping) { //Handling CS-13376 + // host id can change + if (info != null && vm.getState() == State.Running){ + // check for host id changes + Host host = _hostDao.findByGuid(info.getHostUuid()); + if (host != null && (vm.getHostId() == null || host.getId() != vm.getHostId())){ + s_logger.info("Found vm " + vm.getInstanceName() + " with inconsistent host in db, new host is " + host.getId()); + try { + stateTransitTo(vm, VirtualMachine.Event.AgentReportMigrated, host.getId()); + } catch (NoTransitionException e) { + s_logger.warn(e.getMessage()); + } + } + } + /* else if(info == null && vm.getState() == State.Stopping) { //Handling CS-13376 s_logger.warn("Marking the VM as Stopped as it was still stopping on the CS" +vm.getName()); vm.setState(State.Stopped); // Setting the VM as stopped on the DB and clearing it from the host vm.setLastHostId(vm.getHostId()); @@ -1863,7 +1862,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene boolean is_alien_vm = true; long alien_vm_count = -1; for (Map.Entry> entry : newStates.entrySet()) { - is_alien_vm = true; + is_alien_vm = true; for (VirtualMachineGuru vmGuru : vmGurus) { String name = entry.getKey(); VMInstanceVO vm = vmGuru.findByName(name); @@ -1881,8 +1880,8 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } // alien VMs if (is_alien_vm){ - map.put(alien_vm_count--, new AgentVmInfo(entry.getKey(), null, null, entry.getValue().second(), entry.getValue().first())); - s_logger.warn("Found an alien VM " + entry.getKey()); + map.put(alien_vm_count--, new AgentVmInfo(entry.getKey(), null, null, entry.getValue().second(), entry.getValue().first())); + s_logger.warn("Found an alien VM " + entry.getKey()); } } return map; @@ -2267,13 +2266,13 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene Long clusterId = agent.getClusterId(); long agentId = agent.getId(); if (agent.getHypervisorType() == HypervisorType.XenServer) { // only for Xen - StartupRoutingCommand startup = (StartupRoutingCommand) cmd; - HashMap> allStates = startup.getClusterVMStateChanges(); - if (allStates != null){ - this.fullSync(clusterId, allStates); - } - - // initiate the cron job + StartupRoutingCommand startup = (StartupRoutingCommand) cmd; + HashMap> allStates = startup.getClusterVMStateChanges(); + if (allStates != null){ + this.fullSync(clusterId, allStates); + } + + // initiate the cron job ClusterSyncCommand syncCmd = new ClusterSyncCommand(Integer.parseInt(Config.ClusterDeltaSyncInterval.getDefaultValue()), clusterId); try { long seq_no = _agentMgr.send(agentId, new Commands(syncCmd), this); @@ -2340,7 +2339,6 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } catch (Exception e) { s_logger.warn("Caught the following exception on transition checking", e); } finally { - StackMaid.current().exitCleanup(); lock.unlock(); } } @@ -2375,7 +2373,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene public VMInstanceVO findById(long vmId) { return _vmDao.findById(vmId); } - + @Override public void checkIfCanUpgrade(VirtualMachine vmInstance, long newServiceOfferingId) { ServiceOfferingVO newServiceOffering = _offeringDao.findById(newServiceOfferingId); @@ -2387,7 +2385,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene if (!vmInstance.getState().equals(State.Stopped)) { s_logger.warn("Unable to upgrade virtual machine " + vmInstance.toString() + " in state " + vmInstance.getState()); throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + " " + - "in state " + vmInstance.getState() + "in state " + vmInstance.getState() + "; make sure the virtual machine is stopped and not in an error state before upgrading."); } @@ -2395,11 +2393,11 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene if (vmInstance.getServiceOfferingId() == newServiceOffering.getId()) { if (s_logger.isInfoEnabled()) { s_logger.info("Not upgrading vm " + vmInstance.toString() + " since it already has the requested " + - "service offering (" + newServiceOffering.getName() + ")"); + "service offering (" + newServiceOffering.getName() + ")"); } throw new InvalidParameterValueException("Not upgrading vm " + vmInstance.toString() + " since it already " + - "has the requested service offering (" + newServiceOffering.getName() + ")"); + "has the requested service offering (" + newServiceOffering.getName() + ")"); } ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getServiceOfferingId()); @@ -2421,7 +2419,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene "useLocalStorage=" + currentServiceOffering.getUseLocalStorage() + ", target offering useLocalStorage=" + newServiceOffering.getUseLocalStorage()); } - + // if vm is a system vm, check if it is a system service offering, if yes return with error as it cannot be used for user vms if (currentServiceOffering.getSystemUse() != newServiceOffering.getSystemUse()) { throw new InvalidParameterValueException("isSystem property is different for current service offering and new service offering"); @@ -2430,7 +2428,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene // Check that there are enough resources to upgrade the service offering if (!isVirtualMachineUpgradable(vmInstance, newServiceOffering)) { throw new InvalidParameterValueException("Unable to upgrade virtual machine, not enough resources available " + - "for an offering of " + newServiceOffering.getCpu() + " cpu(s) at " + "for an offering of " + newServiceOffering.getCpu() + " cpu(s) at " + newServiceOffering.getSpeed() + " Mhz, and " + newServiceOffering.getRamSize() + " MB of memory"); } @@ -2439,12 +2437,12 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene List newTags = _configMgr.csvTagsToList(newServiceOffering.getTags()); if (!newTags.containsAll(currentTags)) { throw new InvalidParameterValueException("Unable to upgrade virtual machine; the new service offering " + - "does not have all the tags of the " + "does not have all the tags of the " + "current service offering. Current service offering tags: " + currentTags + "; " + "new service " + - "offering tags: " + newTags); + "offering tags: " + newTags); } } - + @Override public boolean upgradeVmDb(long vmId, long serviceOfferingId) { VMInstanceVO vmForUpdate = _vmDao.createForUpdate(); @@ -2455,38 +2453,38 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene vmForUpdate.setServiceOfferingId(newSvcOff.getId()); return _vmDao.update(vmId, vmForUpdate); } - + @Override public NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile requested) throws ConcurrentOperationException, - ResourceUnavailableException, InsufficientCapacityException { - + ResourceUnavailableException, InsufficientCapacityException { + s_logger.debug("Adding vm " + vm + " to network " + network + "; requested nic profile " + requested); VMInstanceVO vmVO = _vmDao.findById(vm.getId()); ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM)); - + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, null, null, null); - + DataCenter dc = _configMgr.getZone(network.getDataCenterId()); Host host = _hostDao.findById(vm.getHostId()); DeployDestination dest = new DeployDestination(dc, null, null, host); - + //check vm state if (vm.getState() == State.Running) { //1) allocate and prepare nic NicProfile nic = _networkMgr.createNicForVm(network, requested, context, vmProfile, true); - + //2) Convert vmProfile to vmTO HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vmProfile.getVirtualMachine().getHypervisorType()); VirtualMachineTO vmTO = hvGuru.implement(vmProfile); - + //3) Convert nicProfile to NicTO NicTO nicTO = toNicTO(nic, vmProfile.getVirtualMachine().getHypervisorType()); - + //4) plug the nic to the vm VirtualMachineGuru vmGuru = getVmGuru(vmVO); - + s_logger.debug("Plugging nic for vm " + vm + " in network " + network); if (vmGuru.plugNic(network, nicTO, vmTO, context, dest)) { s_logger.debug("Nic is plugged successfully for vm " + vm + " in network " + network + ". Vm is a part of network now"); @@ -2509,40 +2507,40 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene @Override public NicTO toNicTO(NicProfile nic, HypervisorType hypervisorType) { HypervisorGuru hvGuru = _hvGuruMgr.getGuru(hypervisorType); - + NicTO nicTO = hvGuru.toNicTO(nic); return nicTO; } - + @Override public boolean removeVmFromNetwork(VirtualMachine vm, Network network, URI broadcastUri) throws ConcurrentOperationException, ResourceUnavailableException { VMInstanceVO vmVO = _vmDao.findById(vm.getId()); ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM)); - + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, null, null, null); - + DataCenter dc = _configMgr.getZone(network.getDataCenterId()); Host host = _hostDao.findById(vm.getHostId()); DeployDestination dest = new DeployDestination(dc, null, null, host); VirtualMachineGuru vmGuru = getVmGuru(vmVO); HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vmProfile.getVirtualMachine().getHypervisorType()); VirtualMachineTO vmTO = hvGuru.implement(vmProfile); - + Nic nic = null; - + if (broadcastUri != null) { nic = _nicsDao.findByNetworkIdInstanceIdAndBroadcastUri(network.getId(), vm.getId(), broadcastUri.toString()); } else { nic = _networkMgr.getNicInNetwork(vm.getId(), network.getId()); } - + NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), _networkMgr.getNetworkRate(network.getId(), vm.getId()), _networkMgr.isSecurityGroupSupportedInNetwork(network), _networkMgr.getNetworkTag(vmProfile.getVirtualMachine().getHypervisorType(), network)); - + //1) Unplug the nic NicTO nicTO = toNicTO(nicProfile, vmProfile.getVirtualMachine().getHypervisorType()); s_logger.debug("Un-plugging nic for vm " + vm + " from network " + network); @@ -2553,14 +2551,14 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene s_logger.warn("Failed to unplug nic for the vm " + vm + " from network " + network); return false; } - + //2) Release the nic _networkMgr.releaseNic(vmProfile, nic); s_logger.debug("Successfully released nic " + nic + "for vm " + vm); - + //3) Remove the nic _networkMgr.removeNic(vmProfile, nic); return result; } - + } diff --git a/server/test/com/cloud/async/TestAsync.java b/server/test/com/cloud/async/TestAsync.java index 187f2e56c57..6f67fe2227f 100644 --- a/server/test/com/cloud/async/TestAsync.java +++ b/server/test/com/cloud/async/TestAsync.java @@ -19,18 +19,13 @@ package com.cloud.async; import java.util.List; -import org.apache.log4j.Logger; - import junit.framework.Assert; -import com.cloud.async.AsyncJobVO; -import com.cloud.cluster.StackMaid; +import org.apache.log4j.Logger; + import com.cloud.cluster.CheckPointVO; import com.cloud.cluster.dao.StackMaidDao; import com.cloud.cluster.dao.StackMaidDaoImpl; -import com.cloud.serializer.Param; -import com.cloud.utils.ActionDelegate; -import com.cloud.utils.Pair; import com.cloud.utils.db.Transaction; import com.cloud.utils.testcase.Log4jEnabledTestCase; @@ -42,15 +37,15 @@ public class TestAsync extends Log4jEnabledTestCase { public static class SampleAsyncResult { @Param(name="name", propName="name") private final String _name; - + @Param private final int count; - + public SampleAsyncResult(String name, int count) { _name = name; this.count = count; } - + public String getName() { return _name; } public int getCount() { return count; } } @@ -60,31 +55,31 @@ public class TestAsync extends Log4jEnabledTestCase { AsyncJobVO job = new AsyncJobVO(1, 1, "TestCmd", null); job.setInstanceType("user_vm"); job.setInstanceId(1000L); - + char[] buf = new char[1024]; for(int i = 0; i < 1024; i++) buf[i] = 'a'; - + job.setResult(new String(buf)); dao.persist(job); - + AsyncJobVO jobVerify = dao.findById(job.getId()); - + Assert.assertTrue(jobVerify.getCmd().equals(job.getCmd())); Assert.assertTrue(jobVerify.getUserId() == 1); Assert.assertTrue(jobVerify.getAccountId() == 1); - + String result = jobVerify.getResult(); for(int i = 0; i < 1024; i++) Assert.assertTrue(result.charAt(i) == 'a'); - + jobVerify = dao.findInstancePendingAsyncJob("user_vm", 1000L); Assert.assertTrue(jobVerify != null); Assert.assertTrue(jobVerify.getCmd().equals(job.getCmd())); Assert.assertTrue(jobVerify.getUserId() == 1); Assert.assertTrue(jobVerify.getAccountId() == 1); } - + public void testSerialization() { List> l; int value = 1; @@ -93,23 +88,23 @@ public class TestAsync extends Log4jEnabledTestCase { Assert.assertTrue(l.get(0).first().equals("result")); Assert.assertTrue(l.get(0).second().equals("1")); l.clear(); - + SampleAsyncResult result = new SampleAsyncResult("vmops", 1); l = SerializerHelper.toPairList(result, "result"); - + Assert.assertTrue(l.size() == 2); Assert.assertTrue(l.get(0).first().equals("name")); Assert.assertTrue(l.get(0).second().equals("vmops")); Assert.assertTrue(l.get(1).first().equals("count")); Assert.assertTrue(l.get(1).second().equals("1")); } - + public void testAsyncResult() { AsyncJobResult result = new AsyncJobResult(1); - + result.setResultObject(100); Assert.assertTrue(result.getResult().equals("java.lang.Integer/100")); - + Object obj = result.getResultObject(); Assert.assertTrue(obj instanceof Integer); Assert.assertTrue(((Integer)obj).intValue() == 100); @@ -119,7 +114,7 @@ public class TestAsync extends Log4jEnabledTestCase { Transaction txn = Transaction.open("testTransaction"); try { txn.start(); - + AsyncJobDao dao = new AsyncJobDaoImpl(); AsyncJobVO job = new AsyncJobVO(1, 1, "TestCmd", null); job.setInstanceType("user_vm"); @@ -131,11 +126,11 @@ public class TestAsync extends Log4jEnabledTestCase { txn.close(); } } - + public void testMorevingian() { int threadCount = 10; final int testCount = 10; - + Thread[] threads = new Thread[threadCount]; for(int i = 0; i < threadCount; i++) { final int threadNum = i + 1; @@ -145,35 +140,35 @@ public class TestAsync extends Log4jEnabledTestCase { Transaction txn = Transaction.open(Transaction.CLOUD_DB); try { AsyncJobDao dao = new AsyncJobDaoImpl(); - + s_logger.info("Thread " + threadNum + " acquiring lock"); AsyncJobVO job = dao.acquire(1L, 30); if(job != null) { s_logger.info("Thread " + threadNum + " acquired lock"); - + try { Thread.sleep(Log4jEnabledTestCase.getRandomMilliseconds(1000, 3000)); } catch (InterruptedException e) { } - + s_logger.info("Thread " + threadNum + " acquiring lock nestly"); AsyncJobVO job2 = dao.acquire(1L, 30); if(job2 != null) { s_logger.info("Thread " + threadNum + " acquired lock nestly"); - + try { Thread.sleep(Log4jEnabledTestCase.getRandomMilliseconds(1000, 3000)); } catch (InterruptedException e) { } - + s_logger.info("Thread " + threadNum + " releasing lock (nestly acquired)"); dao.release(1L); s_logger.info("Thread " + threadNum + " released lock (nestly acquired)"); - + } else { s_logger.info("Thread " + threadNum + " was unable to acquire lock nestly"); } - + s_logger.info("Thread " + threadNum + " releasing lock"); dao.release(1L); s_logger.info("Thread " + threadNum + " released lock"); @@ -183,7 +178,7 @@ public class TestAsync extends Log4jEnabledTestCase { } finally { txn.close(); } - + try { Thread.sleep(Log4jEnabledTestCase.getRandomMilliseconds(1000, 10000)); } catch (InterruptedException e) { @@ -192,11 +187,11 @@ public class TestAsync extends Log4jEnabledTestCase { } }); } - + for(int i = 0; i < threadCount; i++) { threads[i].start(); } - + for(int i = 0; i < threadCount; i++) { try { threads[i].join(); @@ -204,88 +199,83 @@ public class TestAsync extends Log4jEnabledTestCase { } } } - */ - - public void testMaid() { - Transaction txn = Transaction.open(Transaction.CLOUD_DB); - - StackMaidDao dao = new StackMaidDaoImpl(); - dao.pushCleanupDelegate(1L, 0, "delegate1", "Hello, world"); - dao.pushCleanupDelegate(1L, 1, "delegate2", new Long(100)); - dao.pushCleanupDelegate(1L, 2, "delegate3", null); - - CheckPointVO item = dao.popCleanupDelegate(1L); - Assert.assertTrue(item.getDelegate().equals("delegate3")); - Assert.assertTrue(item.getContext() == null); - - item = dao.popCleanupDelegate(1L); - Assert.assertTrue(item.getDelegate().equals("delegate2")); - s_logger.info(item.getContext()); + */ - item = dao.popCleanupDelegate(1L); - Assert.assertTrue(item.getDelegate().equals("delegate1")); - s_logger.info(item.getContext()); - - txn.close(); - } - - public void testMaidClear() { - Transaction txn = Transaction.open(Transaction.CLOUD_DB); - - StackMaidDao dao = new StackMaidDaoImpl(); - dao.pushCleanupDelegate(1L, 0, "delegate1", "Hello, world"); - dao.pushCleanupDelegate(1L, 1, "delegate2", new Long(100)); - dao.pushCleanupDelegate(1L, 2, "delegate3", null); - - dao.clearStack(1L); - Assert.assertTrue(dao.popCleanupDelegate(1L) == null); - txn.close(); - } - - public void testMaidExitCleanup() { - StackMaid.current().push(1L, "com.cloud.async.CleanupDelegate", "Hello, world1"); - StackMaid.current().push(1L, "com.cloud.async.CleanupDelegate", "Hello, world2"); - - StackMaid.current().exitCleanup(1L); - } - - public void testMaidLeftovers() { + public void testMaid() { + Transaction txn = Transaction.open(Transaction.CLOUD_DB); - Thread[] threads = new Thread[3]; - for(int i = 0; i < 3; i++) { - final int threadNum = i+1; - threads[i] = new Thread(new Runnable() { - public void run() { - Transaction txn = Transaction.open(Transaction.CLOUD_DB); - - StackMaidDao dao = new StackMaidDaoImpl(); - dao.pushCleanupDelegate(1L, 0, "delegate-" + threadNum, "Hello, world"); - dao.pushCleanupDelegate(1L, 1, "delegate-" + threadNum, new Long(100)); - dao.pushCleanupDelegate(1L, 2, "delegate-" + threadNum, null); - - txn.close(); - } - }); - - threads[i].start(); - } - - for(int i = 0; i < 3; i++) { - try { - threads[i].join(); - } catch (InterruptedException e) { - } - } + StackMaidDao dao = new StackMaidDaoImpl(); + dao.pushCleanupDelegate(1L, 0, "delegate1", "Hello, world"); + dao.pushCleanupDelegate(1L, 1, "delegate2", new Long(100)); + dao.pushCleanupDelegate(1L, 2, "delegate3", null); - - Transaction txn = Transaction.open(Transaction.CLOUD_DB); - - StackMaidDao dao = new StackMaidDaoImpl(); - List l = dao.listLeftoversByMsid(1L); - for(CheckPointVO maid : l) { - s_logger.info("" + maid.getThreadId() + " " + maid.getDelegate() + " " + maid.getContext()); - } - - txn.close(); - } + CheckPointVO item = dao.popCleanupDelegate(1L); + Assert.assertTrue(item.getDelegate().equals("delegate3")); + Assert.assertTrue(item.getContext() == null); + + item = dao.popCleanupDelegate(1L); + Assert.assertTrue(item.getDelegate().equals("delegate2")); + s_logger.info(item.getContext()); + + item = dao.popCleanupDelegate(1L); + Assert.assertTrue(item.getDelegate().equals("delegate1")); + s_logger.info(item.getContext()); + + txn.close(); + } + + public void testMaidClear() { + Transaction txn = Transaction.open(Transaction.CLOUD_DB); + + StackMaidDao dao = new StackMaidDaoImpl(); + dao.pushCleanupDelegate(1L, 0, "delegate1", "Hello, world"); + dao.pushCleanupDelegate(1L, 1, "delegate2", new Long(100)); + dao.pushCleanupDelegate(1L, 2, "delegate3", null); + + dao.clearStack(1L); + Assert.assertTrue(dao.popCleanupDelegate(1L) == null); + txn.close(); + } + + + public void testMaidLeftovers() { + + Thread[] threads = new Thread[3]; + for(int i = 0; i < 3; i++) { + final int threadNum = i+1; + threads[i] = new Thread(new Runnable() { + @Override + public void run() { + Transaction txn = Transaction.open(Transaction.CLOUD_DB); + + StackMaidDao dao = new StackMaidDaoImpl(); + dao.pushCleanupDelegate(1L, 0, "delegate-" + threadNum, "Hello, world"); + dao.pushCleanupDelegate(1L, 1, "delegate-" + threadNum, new Long(100)); + dao.pushCleanupDelegate(1L, 2, "delegate-" + threadNum, null); + + txn.close(); + } + }); + + threads[i].start(); + } + + for(int i = 0; i < 3; i++) { + try { + threads[i].join(); + } catch (InterruptedException e) { + } + } + + + Transaction txn = Transaction.open(Transaction.CLOUD_DB); + + StackMaidDao dao = new StackMaidDaoImpl(); + List l = dao.listLeftoversByMsid(1L); + for(CheckPointVO maid : l) { + s_logger.info("" + maid.getThreadId() + " " + maid.getDelegate() + " " + maid.getContext()); + } + + txn.close(); + } } From 1ac48bc36cff85f9c1a630628698331f7a2356dd Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 15:27:15 -0800 Subject: [PATCH 24/73] RoleType: public enum to get presently defined static role in CloudStack Signed-off-by: Rohit Yadav --- .../org/apache/cloudstack/acl/RoleType.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 api/src/org/apache/cloudstack/acl/RoleType.java 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; + } +} From f2ae0ae5ae70fbb5f33c004e05e4a17327094651 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 15:29:01 -0800 Subject: [PATCH 25/73] PropertiesUtil: Refactor process config file method in utils, return map of key=value Signed-off-by: Rohit Yadav --- utils/src/com/cloud/utils/PropertiesUtil.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) 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; + } } From 1425736c193c56a51ec59b23e35080bd74926baf Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 15:31:32 -0800 Subject: [PATCH 26/73] AccountManager: Add method to translate account type to role type Signed-off-by: Rohit Yadav --- api/src/com/cloud/user/AccountService.java | 3 +++ .../com/cloud/user/AccountManagerImpl.java | 26 +++++++++++++++++++ .../cloud/user/MockAccountManagerImpl.java | 6 +++++ 3 files changed, 35 insertions(+) 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/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; + } + } From a56f355cebc4b409840a3f2c2deea5bd0f91ab49 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 15:33:52 -0800 Subject: [PATCH 27/73] ApiServer: get role type from account manager using account Signed-off-by: Rohit Yadav --- server/src/com/cloud/api/ApiServer.java | 29 +++---------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 1c1e8ca0e96..73871259504 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -790,37 +790,14 @@ public class ApiServer implements HttpRequestHandler { return true; } - private boolean isCommandAvailable(User user, String commandName) { + private boolean isCommandAvailable(User user, String commandName) + throws PermissionDeniedException { if (user == null) { return false; } Account account = _accountMgr.getAccount(user.getAccountId()); - if (account == null) { - return false; - } - - RoleType roleType = RoleType.Unknown; - 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; - default: - return false; - } - + RoleType roleType = _accountMgr.getRoleType(account); for (APIAccessChecker apiChecker : _apiAccessCheckers) { // Fail the checking if any checker fails to verify if (!apiChecker.canAccessAPI(roleType, commandName)) From c4e890c55d4b106934ff3588adfbbf5a12f48d1e Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 15:43:44 -0800 Subject: [PATCH 28/73] PluggableService: Refactor method to return map of key value pairs - Makes plugins self contained so they decide their properties file format - PluggableService creates the contract that implementing entity will return a properties map which is apiname:rolemask (both are strings) Signed-off-by: Rohit Yadav --- .../server/ManagementServerSimulatorImpl.java | 18 +++++++++--------- .../network/element/CiscoNexusVSMElement.java | 7 +++++-- .../element/F5ExternalLoadBalancerElement.java | 7 +++++-- .../JuniperSRXExternalFirewallElement.java | 7 +++++-- .../network/element/NetscalerElement.java | 6 ++++-- .../network/element/NiciraNvpElement.java | 6 ++++-- .../network/element/VirtualRouterElement.java | 6 ++++-- .../cloud/server/ManagementServerExtImpl.java | 6 ++++-- .../com/cloud/server/ManagementServerImpl.java | 5 +++-- .../utils/component/PluggableService.java | 4 +++- 10 files changed, 46 insertions(+), 26 deletions(-) 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/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..3e0f6efae7c 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -2297,8 +2297,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/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(); } From 0b1c2a5981fea3d4455acee6ac77e9fa84c9724b Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 15:47:59 -0800 Subject: [PATCH 29/73] ApiDiscovery: Fix listApis interface, fix getProperties In case of api discovery, it does not make sense to create a separate properties file If this plugin is enabled in components.xml, a user should be able to discover all the apis accessible to their role. listApis based on role type of caller user Signed-off-by: Rohit Yadav --- .../api/command/user/discovery/ListApisCmd.java | 7 ++++++- .../cloudstack/discovery/ApiDiscoveryService.java | 3 ++- .../cloudstack/discovery/ApiDiscoveryServiceImpl.java | 10 +++++++--- 3 files changed, 15 insertions(+), 5 deletions(-) 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..feab20ac5cf 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,6 +16,9 @@ // under the License. package org.apache.cloudstack.api.command.user.discovery; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.BaseListCmd; @@ -39,7 +42,9 @@ public class ListApisCmd extends BaseListCmd { @Override public void execute() throws ServerApiException { if (_apiDiscoveryService != null) { - ListResponse response = (ListResponse) _apiDiscoveryService.listApis(); + Account caller = UserContext.current().getCaller(); + RoleType roleType = _accountService.getRoleType(UserContext.current().getCaller()); + ListResponse response = (ListResponse) _apiDiscoveryService.listApis(roleType); if (response == null) { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Api Discovery plugin was unable to find and process any apis"); } diff --git a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java index 96ea3ee4d34..a1d440e9ccf 100644 --- a/plugins/api/discovery/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 listApis(); + ListResponse listApis(RoleType roleType); } 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..2bc17bdbf4d 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,9 @@ // under the License. package org.apache.cloudstack.discovery; +import com.cloud.utils.PropertiesUtil; import com.cloud.utils.ReflectUtil; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.BaseAsyncCmd; @@ -108,12 +110,14 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { } @Override - public ListResponse listApis() { + public ListResponse listApis(RoleType roleType) { return _discoveryResponse; } @Override - public String[] getPropertiesFiles() { - return new String[] { "api-discovery_commands.properties" }; + public Map getProperties() { + Map apiDiscoveryPropertyMap = new HashMap(); + apiDiscoveryPropertyMap.put("listApis", "15"); + return apiDiscoveryPropertyMap; } } From 8f26e171e64726a726fa146f9c02433e48c0ba60 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 15:51:46 -0800 Subject: [PATCH 30/73] ManagementServerImpl: Fix missing import of PropertiesUtil Signed-off-by: Rohit Yadav --- server/src/com/cloud/server/ManagementServerImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 3e0f6efae7c..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; From 345c179e77dce7ba471f6846fac785bd34bda294 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 15:53:18 -0800 Subject: [PATCH 31/73] plugins: Check access based on roleType, remove unnecessary properties.in file - Fix StaticRoleBasedAPIAccessChecker to check api access based on roletype - Remove properties file which is not needed now for api discovery plugin Signed-off-by: Rohit Yadav --- .../api-discovery_commands.properties.in | 23 --- .../acl/StaticRoleBasedAPIAccessChecker.java | 136 +++++++----------- 2 files changed, 48 insertions(+), 111 deletions(-) delete mode 100644 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 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/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 43ca403f890..689540aa291 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,28 +16,23 @@ // 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 javax.ejb.Local; -import javax.naming.ConfigurationException; - -import org.apache.cloudstack.acl.APIAccessChecker; -import org.apache.cloudstack.acl.RoleType; -import static org.apache.cloudstack.acl.RoleType.*; -import org.apache.log4j.Logger; - import com.cloud.exception.PermissionDeniedException; import com.cloud.server.ManagementServer; -import com.cloud.utils.PropertiesUtil; 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 static org.apache.cloudstack.acl.RoleType.*; +import org.apache.log4j.Logger; + // This is the default API access checker that grab's the user's account // based on the account type, access is granted @Local(value=APIAccessChecker.class) @@ -60,35 +55,29 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIA } @Override - public boolean canAccessAPI(RoleType roleType, String apiCommandName) - throws PermissionDeniedException{ + public boolean canAccessAPI(RoleType roleType, String commandName) + throws PermissionDeniedException { - boolean commandExists = s_allCommands.contains(apiCommandName); + boolean commandExists = s_allCommands.contains(commandName); + boolean commandAccessible = false; - if(commandExists) { - return isCommandAvailableForAccount(roleType, apiCommandName); + if (commandExists) { + switch (roleType) { + case Admin: + commandAccessible = s_adminCommands.contains(commandName); + break; + case DomainAdmin: + commandAccessible = s_resellerCommands.contains(commandName); + break; + case ResourceAdmin: + commandAccessible = s_resourceDomainAdminCommands.contains(commandName); + break; + case User: + commandAccessible = s_userCommands.contains(commandName); + break; + } } - - return commandExists; - } - - private static boolean isCommandAvailableForAccount(RoleType roleType, String commandName) { - boolean isCommandAvailable = false; - switch (roleType) { - case Admin: - isCommandAvailable = s_adminCommands.contains(commandName); - break; - case DomainAdmin: - isCommandAvailable = s_resellerCommands.contains(commandName); - break; - case ResourceAdmin: - isCommandAvailable = s_resourceDomainAdminCommands.contains(commandName); - break; - case User: - isCommandAvailable = s_userCommands.contains(commandName); - break; - } - return isCommandAvailable; + return commandExists && commandAccessible; } @Override @@ -100,69 +89,40 @@ 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 config) { + for (Map.Entry entry: config.entrySet()) { + String apiName = entry.getKey(); + String roleMask = entry.getValue(); try { - short cmdPermissions = Short.parseShort(mask); + short cmdPermissions = Short.parseShort(roleMask); if ((cmdPermissions & Admin.getValue()) != 0) { - s_adminCommands.add((String) key); + s_adminCommands.add(apiName); } if ((cmdPermissions & ResourceAdmin.getValue()) != 0) { - s_resourceDomainAdminCommands.add((String) key); + s_resourceDomainAdminCommands.add(apiName); } if ((cmdPermissions & DomainAdmin.getValue()) != 0) { - s_resellerCommands.add((String) key); + s_resellerCommands.add(apiName); } if ((cmdPermissions & User.getValue()) != 0) { - s_userCommands.add((String) key); + s_userCommands.add(apiName); } - 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()); } } + s_allCommands.addAll(s_adminCommands); + s_allCommands.addAll(s_resourceDomainAdminCommands); + s_allCommands.addAll(s_userCommands); + s_allCommands.addAll(s_resellerCommands); } - } From e63e35250876a4f985a707ac52bdef34465a2470 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 16:08:16 -0800 Subject: [PATCH 32/73] ApiServer: Log a better message if api access fails and debug it Signed-off-by: Rohit Yadav --- server/src/com/cloud/api/ApiServer.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 73871259504..be3c08716cc 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -552,14 +552,14 @@ public class ApiServer implements HttpRequestHandler { if (userId != null) { User user = ApiDBUtils.findUserById(userId); 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"); + 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"); } } @@ -653,8 +653,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 From 757e1a931b94ff74206a6ba6ce2fe89081f93ebc Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Thu, 10 Jan 2013 17:19:30 -0800 Subject: [PATCH 33/73] cleanup warnings in utils --- .../acl/StaticRoleBasedAPIAccessChecker.java | 2 + pom.xml | 32 +++- server/src/com/cloud/api/ApiServer.java | 3 +- .../cloud/servlet/CloudStartupServlet.java | 3 - utils/src/com/cloud/utils/UriUtils.java | 52 +++--- .../crypt/EncryptionSecretKeyChecker.java | 156 ++++++++--------- .../crypt/EncryptionSecretKeySender.java | 68 ++++---- .../exception/RuntimeCloudException.java | 65 +++---- .../src/com/cloud/utils/fsm/FiniteState2.java | 2 +- .../utils/log/CglibThrowableRenderer.java | 38 +---- utils/src/com/cloud/utils/net/MacAddress.java | 27 ++- utils/src/com/cloud/utils/net/NetUtils.java | 42 +---- utils/src/com/cloud/utils/net/NfsUtils.java | 19 +-- .../com/cloud/utils/nio/HandlerFactory.java | 1 - utils/src/com/cloud/utils/nio/Link.java | 161 +++++++++--------- .../utils/security/CertificateHelper.java | 129 +++++++------- 16 files changed, 379 insertions(+), 421 deletions(-) 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 32de0705f53..7c69301c62f 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 @@ -32,6 +32,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.exception.PermissionDeniedException; import com.cloud.user.AccountManager; @@ -40,6 +41,7 @@ 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 +@Component @Local(value=APIAccessChecker.class) public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIAccessChecker { diff --git a/pom.xml b/pom.xml index 16310991974..4b5e3cfe54e 100644 --- a/pom.xml +++ b/pom.xml @@ -43,7 +43,7 @@ - true + true 1.6 UTF-8 @@ -296,6 +296,36 @@ install + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + org.apache.maven.plugins + + + maven-antrun-plugin + + [1.7,) + + run + + + + + + + + + + org.apache.tomcat.maven tomcat7-maven-plugin diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index c0eedc15751..0c64e588a40 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -155,8 +155,7 @@ public class ApiServer implements HttpRequestHandler { @Inject List _pluggableServices; @Inject IdentityDao _identityDao; - @Inject - protected List _apiAccessCheckers; + @Inject List _apiAccessCheckers; private Account _systemAccount = null; private User _systemUser = null; diff --git a/server/src/com/cloud/servlet/CloudStartupServlet.java b/server/src/com/cloud/servlet/CloudStartupServlet.java index fefcb2fda73..eae211b63f7 100755 --- a/server/src/com/cloud/servlet/CloudStartupServlet.java +++ b/server/src/com/cloud/servlet/CloudStartupServlet.java @@ -37,10 +37,7 @@ public class CloudStartupServlet extends HttpServlet implements ServletContextLi @Override public void init() throws ServletException { - // Save Configuration Values - //ComponentLocator loc = ComponentLocator.getLocator(ConfigurationServer.Name); ConfigurationServer c = (ConfigurationServer)ComponentContext.getComponent(ConfigurationServer.Name); - //ConfigurationServer c = new ConfigurationServerImpl(); try { c.persistDefaultValues(); ManagementServer ms = (ManagementServer)ComponentContext.getComponent(ManagementServer.Name); diff --git a/utils/src/com/cloud/utils/UriUtils.java b/utils/src/com/cloud/utils/UriUtils.java index 4a56988759f..a8b5ccb0934 100644 --- a/utils/src/com/cloud/utils/UriUtils.java +++ b/utils/src/com/cloud/utils/UriUtils.java @@ -32,7 +32,7 @@ public class UriUtils { throw new CloudRuntimeException("Unable to form nfs URI: " + host + " - " + path); } } - + public static String formIscsiUri(String host, String iqn, Integer lun) { try { String path = iqn; @@ -48,34 +48,34 @@ public class UriUtils { public static String formFileUri(String path) { File file = new File(path); - + return file.toURI().toString(); } - + // a simple URI component helper (Note: it does not deal with URI paramemeter area) public static String encodeURIComponent(String url) { - int schemeTail = url.indexOf("://"); - - int pathStart = 0; - if(schemeTail > 0) - pathStart = url.indexOf('/', schemeTail + 3); - else - pathStart = url.indexOf('/'); - - if(pathStart > 0) { - String[] tokens = url.substring(pathStart + 1).split("/"); - if(tokens != null) { - StringBuffer sb = new StringBuffer(); - sb.append(url.substring(0, pathStart)); - for(String token : tokens) { - sb.append("/").append(URLEncoder.encode(token)); - } - - return sb.toString(); - } - } - - // no need to do URL component encoding - return url; + int schemeTail = url.indexOf("://"); + + int pathStart = 0; + if(schemeTail > 0) + pathStart = url.indexOf('/', schemeTail + 3); + else + pathStart = url.indexOf('/'); + + if(pathStart > 0) { + String[] tokens = url.substring(pathStart + 1).split("/"); + if(tokens != null) { + StringBuffer sb = new StringBuffer(); + sb.append(url.substring(0, pathStart)); + for(String token : tokens) { + sb.append("/").append(URLEncoder.encode(token)); + } + + return sb.toString(); + } + } + + // no need to do URL component encoding + return url; } } diff --git a/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java b/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java index 78d3200d113..a9c670d6aa9 100755 --- a/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java +++ b/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java @@ -40,108 +40,108 @@ import com.cloud.utils.exception.CloudRuntimeException; @Local(value = {SystemIntegrityChecker.class}) public class EncryptionSecretKeyChecker implements SystemIntegrityChecker { - - private static final Logger s_logger = Logger.getLogger(EncryptionSecretKeyChecker.class); - + + private static final Logger s_logger = Logger.getLogger(EncryptionSecretKeyChecker.class); + private static final String s_keyFile = "/etc/cloud/management/key"; private static final String s_envKey = "CLOUD_SECRET_KEY"; private static StandardPBEStringEncryptor s_encryptor = new StandardPBEStringEncryptor(); private static boolean s_useEncryption = false; - + @Override public void check() { - //Get encryption type from db.properties - final File dbPropsFile = PropertiesUtil.findConfigFile("db.properties"); + //Get encryption type from db.properties + final File dbPropsFile = PropertiesUtil.findConfigFile("db.properties"); final Properties dbProps = new Properties(); try { - dbProps.load(new FileInputStream(dbPropsFile)); + dbProps.load(new FileInputStream(dbPropsFile)); - final String encryptionType = dbProps.getProperty("db.cloud.encryption.type"); - - s_logger.debug("Encryption Type: "+ encryptionType); + final String encryptionType = dbProps.getProperty("db.cloud.encryption.type"); - if(encryptionType == null || encryptionType.equals("none")){ - return; - } - - s_encryptor.setAlgorithm("PBEWithMD5AndDES"); - String secretKey = null; - - SimpleStringPBEConfig stringConfig = new SimpleStringPBEConfig(); - - if(encryptionType.equals("file")){ - try { - BufferedReader in = new BufferedReader(new FileReader(s_keyFile)); - secretKey = in.readLine(); - //Check for null or empty secret key - } catch (FileNotFoundException e) { - throw new CloudRuntimeException("File containing secret key not found: "+s_keyFile, e); - } catch (IOException e) { - throw new CloudRuntimeException("Error while reading secret key from: "+s_keyFile, e); - } - - if(secretKey == null || secretKey.isEmpty()){ - throw new CloudRuntimeException("Secret key is null or empty in file "+s_keyFile); - } - - } else if(encryptionType.equals("env")){ - secretKey = System.getenv(s_envKey); - if(secretKey == null || secretKey.isEmpty()){ - throw new CloudRuntimeException("Environment variable "+s_envKey+" is not set or empty"); - } - } else if(encryptionType.equals("web")){ - ServerSocket serverSocket = null; - int port = 8097; - try { + s_logger.debug("Encryption Type: "+ encryptionType); + + if(encryptionType == null || encryptionType.equals("none")){ + return; + } + + s_encryptor.setAlgorithm("PBEWithMD5AndDES"); + String secretKey = null; + + SimpleStringPBEConfig stringConfig = new SimpleStringPBEConfig(); + + if(encryptionType.equals("file")){ + try { + BufferedReader in = new BufferedReader(new FileReader(s_keyFile)); + secretKey = in.readLine(); + //Check for null or empty secret key + } catch (FileNotFoundException e) { + throw new CloudRuntimeException("File containing secret key not found: "+s_keyFile, e); + } catch (IOException e) { + throw new CloudRuntimeException("Error while reading secret key from: "+s_keyFile, e); + } + + if(secretKey == null || secretKey.isEmpty()){ + throw new CloudRuntimeException("Secret key is null or empty in file "+s_keyFile); + } + + } else if(encryptionType.equals("env")){ + secretKey = System.getenv(s_envKey); + if(secretKey == null || secretKey.isEmpty()){ + throw new CloudRuntimeException("Environment variable "+s_envKey+" is not set or empty"); + } + } else if(encryptionType.equals("web")){ + ServerSocket serverSocket = null; + int port = 8097; + try { serverSocket = new ServerSocket(port); } catch (IOException ioex) { - throw new CloudRuntimeException("Error initializing secret key reciever", ioex); + throw new CloudRuntimeException("Error initializing secret key reciever", ioex); } - s_logger.info("Waiting for admin to send secret key on port "+port); - Socket clientSocket = null; - try { - clientSocket = serverSocket.accept(); - } catch (IOException e) { - throw new CloudRuntimeException("Accept failed on "+port); - } - PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); - BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); - String inputLine, outputLine; - if ((inputLine = in.readLine()) != null) { - secretKey = inputLine; - } - out.close(); - in.close(); - clientSocket.close(); - serverSocket.close(); - } else { - throw new CloudRuntimeException("Invalid encryption type: "+encryptionType); - } + s_logger.info("Waiting for admin to send secret key on port "+port); + Socket clientSocket = null; + try { + clientSocket = serverSocket.accept(); + } catch (IOException e) { + throw new CloudRuntimeException("Accept failed on "+port); + } + PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); + BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); + String inputLine; + if ((inputLine = in.readLine()) != null) { + secretKey = inputLine; + } + out.close(); + in.close(); + clientSocket.close(); + serverSocket.close(); + } else { + throw new CloudRuntimeException("Invalid encryption type: "+encryptionType); + } - stringConfig.setPassword(secretKey); - s_encryptor.setConfig(stringConfig); - s_useEncryption = true; + stringConfig.setPassword(secretKey); + s_encryptor.setConfig(stringConfig); + s_useEncryption = true; } catch (FileNotFoundException e) { - throw new CloudRuntimeException("File db.properties not found", e); + throw new CloudRuntimeException("File db.properties not found", e); } catch (IOException e) { - throw new CloudRuntimeException("Error while reading db.properties", e); + throw new CloudRuntimeException("Error while reading db.properties", e); } } - + public static StandardPBEStringEncryptor getEncryptor() { return s_encryptor; } - + public static boolean useEncryption(){ - return s_useEncryption; + return s_useEncryption; } - + //Initialize encryptor for migration during secret key change public static void initEncryptorForMigration(String secretKey){ - s_encryptor.setAlgorithm("PBEWithMD5AndDES"); - SimpleStringPBEConfig stringConfig = new SimpleStringPBEConfig(); - stringConfig.setPassword(secretKey); - s_encryptor.setConfig(stringConfig); - s_useEncryption = true; + s_encryptor.setAlgorithm("PBEWithMD5AndDES"); + SimpleStringPBEConfig stringConfig = new SimpleStringPBEConfig(); + stringConfig.setPassword(secretKey); + s_encryptor.setConfig(stringConfig); + s_useEncryption = true; } } diff --git a/utils/src/com/cloud/utils/crypt/EncryptionSecretKeySender.java b/utils/src/com/cloud/utils/crypt/EncryptionSecretKeySender.java index 390443768e1..2dc865cfec0 100755 --- a/utils/src/com/cloud/utils/crypt/EncryptionSecretKeySender.java +++ b/utils/src/com/cloud/utils/crypt/EncryptionSecretKeySender.java @@ -16,8 +16,6 @@ // under the License. package com.cloud.utils.crypt; -import java.io.BufferedReader; -import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; @@ -26,39 +24,37 @@ import com.cloud.utils.NumbersUtil; public class EncryptionSecretKeySender { - public static void main(String args[]){ - try { + public static void main(String args[]){ + try { - // Create a socket to the host - String hostname = "localhost"; - int port = 8097; - - if(args.length == 2){ - hostname = args[0]; - port = NumbersUtil.parseInt(args[1], port); - } - - - InetAddress addr = InetAddress.getByName(hostname); - Socket socket = new Socket(addr, port); - PrintWriter out = new PrintWriter(socket.getOutputStream(), true); - BufferedReader in = new BufferedReader(new InputStreamReader( - socket.getInputStream())); - java.io.BufferedReader stdin = new java.io.BufferedReader(new java.io.InputStreamReader(System.in)); - String validationWord = "cloudnine"; - String validationInput = ""; - while(!validationWord.equals(validationInput)){ - System.out.print("Enter Validation Word:"); - validationInput = stdin.readLine(); - System.out.println(); - } - System.out.print("Enter Secret Key:"); - String input = stdin.readLine(); - if (input != null) { - out.println(input); - } - } catch (Exception e) { - System.out.print("Exception while sending secret key "+e); - } - } + // Create a socket to the host + String hostname = "localhost"; + int port = 8097; + + if(args.length == 2){ + hostname = args[0]; + port = NumbersUtil.parseInt(args[1], port); + } + + + InetAddress addr = InetAddress.getByName(hostname); + Socket socket = new Socket(addr, port); + PrintWriter out = new PrintWriter(socket.getOutputStream(), true); + java.io.BufferedReader stdin = new java.io.BufferedReader(new java.io.InputStreamReader(System.in)); + String validationWord = "cloudnine"; + String validationInput = ""; + while(!validationWord.equals(validationInput)){ + System.out.print("Enter Validation Word:"); + validationInput = stdin.readLine(); + System.out.println(); + } + System.out.print("Enter Secret Key:"); + String input = stdin.readLine(); + if (input != null) { + out.println(input); + } + } catch (Exception e) { + System.out.print("Exception while sending secret key "+e); + } + } } diff --git a/utils/src/com/cloud/utils/exception/RuntimeCloudException.java b/utils/src/com/cloud/utils/exception/RuntimeCloudException.java index 52229800785..a2de5161596 100644 --- a/utils/src/com/cloud/utils/exception/RuntimeCloudException.java +++ b/utils/src/com/cloud/utils/exception/RuntimeCloudException.java @@ -16,9 +16,10 @@ // under the License. package com.cloud.utils.exception; -import com.cloud.utils.AnnotationHelper; import java.util.ArrayList; +import com.cloud.utils.AnnotationHelper; + /** * by the API response serializer. Any exceptions that are thrown by * class, which extends Exception instead of RuntimeException like this @@ -27,20 +28,22 @@ import java.util.ArrayList; public class RuntimeCloudException extends RuntimeException { - // This holds a list of uuids and their names. Add uuid:fieldname pairs - protected ArrayList idList = new ArrayList(); + private static final long serialVersionUID = 1783478684819198850L; - protected int csErrorCode; + // This holds a list of uuids and their names. Add uuid:fieldname pairs + protected ArrayList idList = new ArrayList(); - public void addProxyObject(String uuid) { - idList.add(uuid); - return; - } + protected int csErrorCode; - public RuntimeCloudException(String message) { - super(message); - setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName())); - } + public void addProxyObject(String uuid) { + idList.add(uuid); + return; + } + + public RuntimeCloudException(String message) { + super(message); + setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName())); + } public RuntimeCloudException(String message, Throwable cause) { super(message, cause); @@ -48,28 +51,28 @@ public class RuntimeCloudException extends RuntimeException { } public void addProxyObject(Object voObj, Long id, String idFieldName) { - // Get the VO object's table name. - String tablename = AnnotationHelper.getTableName(voObj); - if (tablename != null) { - addProxyObject(tablename, id, idFieldName); - } - return; + // Get the VO object's table name. + String tablename = AnnotationHelper.getTableName(voObj); + if (tablename != null) { + addProxyObject(tablename, id, idFieldName); + } + return; } - public RuntimeCloudException() { - super(); - setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName())); - } + public RuntimeCloudException() { + super(); + setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName())); + } - public ArrayList getIdProxyList() { - return idList; - } + public ArrayList getIdProxyList() { + return idList; + } - public void setCSErrorCode(int cserrcode) { - this.csErrorCode = cserrcode; - } + public void setCSErrorCode(int cserrcode) { + this.csErrorCode = cserrcode; + } - public int getCSErrorCode() { - return this.csErrorCode; - } + public int getCSErrorCode() { + return this.csErrorCode; + } } diff --git a/utils/src/com/cloud/utils/fsm/FiniteState2.java b/utils/src/com/cloud/utils/fsm/FiniteState2.java index 585521d4bf3..0147ba45786 100755 --- a/utils/src/com/cloud/utils/fsm/FiniteState2.java +++ b/utils/src/com/cloud/utils/fsm/FiniteState2.java @@ -22,7 +22,7 @@ import java.util.Set; public interface FiniteState2 { - StateMachine2 getStateMachine(); + StateMachine2> getStateMachine(); T getNextState(ChangeEvent e) throws NoTransitionException; diff --git a/utils/src/com/cloud/utils/log/CglibThrowableRenderer.java b/utils/src/com/cloud/utils/log/CglibThrowableRenderer.java index 06cd0c36906..83c1dce4a52 100644 --- a/utils/src/com/cloud/utils/log/CglibThrowableRenderer.java +++ b/utils/src/com/cloud/utils/log/CglibThrowableRenderer.java @@ -17,7 +17,6 @@ package com.cloud.utils.log; import java.io.PrintWriter; -import java.lang.reflect.Method; import java.util.ArrayList; import org.apache.log4j.spi.ThrowableRenderer; @@ -35,26 +34,11 @@ import org.apache.log4j.spi.ThrowableRenderer; * */ public class CglibThrowableRenderer implements ThrowableRenderer { - /** - * Throwable.getStackTrace() method. - */ - private Method getStackTraceMethod; - /** - * StackTraceElement.getClassName() method. - */ - private Method getClassNameMethod; - /** * Construct new instance. */ public CglibThrowableRenderer() { - try { - Class[] noArgs = null; - getStackTraceMethod = Throwable.class.getMethod("getStackTrace", noArgs); - Class ste = Class.forName("java.lang.StackTraceElement"); - getClassNameMethod = ste.getMethod("getClassName", noArgs); - } catch (Exception ex) { - } + super(); } @Override @@ -94,24 +78,4 @@ public class CglibThrowableRenderer implements ThrowableRenderer { return null; } } - - /** - * Find class given class name. - * - * @param className class name, may not be null. - * @return class, will not be null. - * @throws ClassNotFoundException thrown if class can not be found. - */ - private Class findClass(final String className) throws ClassNotFoundException { - try { - return Thread.currentThread().getContextClassLoader().loadClass(className); - } catch (ClassNotFoundException e) { - try { - return Class.forName(className); - } catch (ClassNotFoundException e1) { - return getClass().getClassLoader().loadClass(className); - } - } - } - } diff --git a/utils/src/com/cloud/utils/net/MacAddress.java b/utils/src/com/cloud/utils/net/MacAddress.java index f81127c6660..15350c8b4d3 100755 --- a/utils/src/com/cloud/utils/net/MacAddress.java +++ b/utils/src/com/cloud/utils/net/MacAddress.java @@ -60,16 +60,16 @@ public class MacAddress { StringBuilder buff = new StringBuilder(); Formatter formatter = new Formatter(buff); formatter.format("%02x%s%02x%s%02x%s%02x%s%02x%s%02x", - _addr >> 40 & 0xff, separator, - _addr >> 32 & 0xff, separator, - _addr >> 24 & 0xff, separator, - _addr >> 16 & 0xff, separator, - _addr >> 8 & 0xff, separator, - _addr & 0xff); + _addr >> 40 & 0xff, separator, + _addr >> 32 & 0xff, separator, + _addr >> 24 & 0xff, separator, + _addr >> 16 & 0xff, separator, + _addr >> 8 & 0xff, separator, + _addr & 0xff); return buff.toString(); - + /* - + String str = Long.toHexString(_addr); for (int i = str.length() - 1; i >= 0; i--) { @@ -79,11 +79,11 @@ public class MacAddress { } } return buff.reverse().toString(); - */ + */ } @Override - public String toString() { + public String toString() { return toString(":"); } @@ -102,7 +102,7 @@ public class MacAddress { } else if (osname.startsWith("Solaris") || osname.startsWith("SunOS")) { // Solaris code must appear before the generic code String hostName = MacAddress.getFirstLineOfCommand(new String[] { "uname", - "-n"}); + "-n"}); if (hostName != null) { p = Runtime.getRuntime().exec(new String[] { "/usr/sbin/arp", hostName}, null); } @@ -163,7 +163,7 @@ public class MacAddress { clockSeqAndNode |= (long) (Math.random() * 0x7FFFFFFF); } } - + s_address = new MacAddress(clockSeqAndNode); } @@ -262,9 +262,6 @@ public class MacAddress { System.out.println("addr in char is " + addr.toString(":")); } -private static final char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', - '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - /** * Parses a long from a hex encoded number. This method will skip * all characters that are not 0-9 and a-f (the String is lower cased first). diff --git a/utils/src/com/cloud/utils/net/NetUtils.java b/utils/src/com/cloud/utils/net/NetUtils.java index c456cdcca8e..005fe239c49 100755 --- a/utils/src/com/cloud/utils/net/NetUtils.java +++ b/utils/src/com/cloud/utils/net/NetUtils.java @@ -17,15 +17,12 @@ package com.cloud.utils.net; import java.io.BufferedReader; -import java.io.File; import java.io.InputStreamReader; import java.lang.reflect.Array; import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.SocketException; -import java.net.URISyntaxException; -import java.net.URL; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Formatter; @@ -39,7 +36,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.log4j.Logger; -import org.apache.log4j.xml.DOMConfigurator; import com.cloud.utils.IteratorUtil; import com.cloud.utils.Pair; @@ -681,7 +677,7 @@ public class NetUtils { if (avoid.size() >= range) { return -1; } - + //Reduce the range by the size of the avoid set //e.g., cidr = 192.168.10.0, size = /24, avoid = 192.168.10.1, 192.168.10.20, 192.168.10.254 // range = 2^8 - 1 - 3 = 252 @@ -690,9 +686,9 @@ public class NetUtils { long ip = startIp + next; for (Long avoidable : avoid) { if (ip >= avoidable) { - ip++; + ip++; } else { - break; + break; } } @@ -796,7 +792,7 @@ public class NetUtils { long shift = 32 - cidrBLong[1]; return ((cidrALong[0] >> shift) == (cidrBLong[0] >> shift)); } - + public static Long[] cidrToLong(String cidr) { if (cidr == null || cidr.isEmpty()) { return null; @@ -960,26 +956,6 @@ public class NetUtils { return Integer.toString(portRange[0]) + ":" + Integer.toString(portRange[1]); } - // test only - private static void configLog4j() { - URL configUrl = System.class.getResource("/conf/log4j-cloud.xml"); - if (configUrl != null) { - System.out.println("Configure log4j using log4j-cloud.xml"); - - try { - File file = new File(configUrl.toURI()); - - System.out.println("Log4j configuration from : " + file.getAbsolutePath()); - DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000); - } catch (URISyntaxException e) { - System.out.println("Unable to convert log4j configuration Url to URI"); - } - // DOMConfigurator.configure(configUrl); - } else { - System.out.println("Configure log4j with default properties"); - } - } - public static boolean verifyDomainNameLabel(String hostName, boolean isHostName) { // must be between 1 and 63 characters long and may contain only the ASCII letters 'a' through 'z' (in a @@ -1068,7 +1044,7 @@ public class NetUtils { return true; } - + public static boolean isNetworksOverlap(String cidrA, String cidrB) { Long[] cidrALong = cidrToLong(cidrA); Long[] cidrBLong = cidrToLong(cidrB); @@ -1122,7 +1098,7 @@ public class NetUtils { } return true; } - + public static boolean validateIcmpType(long icmpType) { //Source - http://www.erg.abdn.ac.uk/~gorry/course/inet-pages/icmp-code.html if(!(icmpType >=0 && icmpType <=255)) { @@ -1131,15 +1107,15 @@ public class NetUtils { } return true; } - + public static boolean validateIcmpCode(long icmpCode) { - + //Source - http://www.erg.abdn.ac.uk/~gorry/course/inet-pages/icmp-code.html if(!(icmpCode >=0 && icmpCode <=15)) { s_logger.warn("Icmp code should be within 0-15 range"); return false; } - + return true; } } diff --git a/utils/src/com/cloud/utils/net/NfsUtils.java b/utils/src/com/cloud/utils/net/NfsUtils.java index 7318383e9ed..19ff05594a0 100644 --- a/utils/src/com/cloud/utils/net/NfsUtils.java +++ b/utils/src/com/cloud/utils/net/NfsUtils.java @@ -21,18 +21,17 @@ import java.net.URI; import java.net.URISyntaxException; public class NfsUtils { - + public static String url2Mount(String urlStr) throws URISyntaxException { URI url; url = new URI(urlStr); - int port = url.getPort(); return url.getHost() + ":" + url.getPath(); } - + public static String uri2Mount(URI uri) { return uri.getHost() + ":" + uri.getPath(); } - + public static String url2PathSafeString(String urlStr) { String safe = urlStr.replace(File.separatorChar, '-'); safe = safe.replace("?", ""); @@ -41,13 +40,13 @@ public class NfsUtils { safe = safe.replace("/", ""); return safe; } - + public static String getHostPart(String nfsPath) { - String toks[] = nfsPath.split(":"); - if (toks != null && toks.length == 2) { - return toks[0]; - } - return null; + String toks[] = nfsPath.split(":"); + if (toks != null && toks.length == 2) { + return toks[0]; + } + return null; } } diff --git a/utils/src/com/cloud/utils/nio/HandlerFactory.java b/utils/src/com/cloud/utils/nio/HandlerFactory.java index 0dcc83fb442..9cf218d99eb 100755 --- a/utils/src/com/cloud/utils/nio/HandlerFactory.java +++ b/utils/src/com/cloud/utils/nio/HandlerFactory.java @@ -16,7 +16,6 @@ // under the License. package com.cloud.utils.nio; -import java.util.List; /** * WorkerFactory creates and selects workers. diff --git a/utils/src/com/cloud/utils/nio/Link.java b/utils/src/com/cloud/utils/nio/Link.java index 3e3da6c11d5..4b041f5b265 100755 --- a/utils/src/com/cloud/utils/nio/Link.java +++ b/utils/src/com/cloud/utils/nio/Link.java @@ -16,18 +16,15 @@ // under the License. package com.cloud.utils.nio; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.InetSocketAddress; import java.nio.ByteBuffer; -import java.nio.channels.Channels; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; -import java.nio.channels.WritableByteChannel; import java.security.KeyStore; import java.util.concurrent.ConcurrentLinkedQueue; @@ -35,10 +32,10 @@ import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.SSLEngineResult.HandshakeStatus; import org.apache.log4j.Logger; @@ -48,7 +45,7 @@ import com.cloud.utils.PropertiesUtil; */ public class Link { private static final Logger s_logger = Logger.getLogger(Link.class); - + private final InetSocketAddress _addr; private final NioConnection _connection; private SelectionKey _key; @@ -58,7 +55,7 @@ public class Link { private Object _attach; private boolean _readHeader; private boolean _gotFollowingPacket; - + private SSLEngine _sslEngine; public Link(InetSocketAddress addr, NioConnection connection) { @@ -71,23 +68,23 @@ public class Link { _readHeader = true; _gotFollowingPacket = false; } - + public Link (Link link) { this(link._addr, link._connection); } - + public Object attachment() { return _attach; } - + public void attach(Object attach) { _attach = attach; } - + public void setKey(SelectionKey key) { _key = key; } - + public void setSSLEngine(SSLEngine sslEngine) { _sslEngine = sslEngine; } @@ -105,19 +102,19 @@ public class Link { synchronized(buff) { buff.clear(); buff.limit(4); - + while (buff.hasRemaining()) { if (ch.read(buff) == -1) { throw new IOException("Connection closed with -1 on reading size."); } } - + buff.flip(); - + int length = buff.getInt(); ByteArrayOutputStream output = new ByteArrayOutputStream(length); WritableByteChannel outCh = Channels.newChannel(output); - + int count = 0; while (count < length) { buff.clear(); @@ -129,19 +126,19 @@ public class Link { buff.flip(); outCh.write(buff); } - + return output.toByteArray(); } } - */ - + */ + private static void doWrite(SocketChannel ch, ByteBuffer[] buffers, SSLEngine sslEngine) throws IOException { SSLSession sslSession = sslEngine.getSession(); ByteBuffer pkgBuf = ByteBuffer.allocate(sslSession.getPacketBufferSize() + 40); SSLEngineResult engResult; ByteBuffer headBuf = ByteBuffer.allocate(4); - + int totalLen = 0; for (ByteBuffer buffer : buffers) { totalLen += buffer.limit(); @@ -157,7 +154,7 @@ public class Link { engResult.getStatus() != SSLEngineResult.Status.OK) { throw new IOException("SSL: SSLEngine return bad result! " + engResult); } - + processedLen = 0; for (ByteBuffer buffer : buffers) { processedLen += buffer.position(); @@ -189,7 +186,7 @@ public class Link { } } } - + /** * write method to write to a socket. This method writes to completion so * it doesn't follow the nio standard. We use this to make sure we write @@ -204,21 +201,21 @@ public class Link { doWrite(ch, buffers, sslEngine); } } - + /* SSL has limitation of 16k, we may need to split packets. 18000 is 16k + some extra SSL informations */ protected static final int MAX_SIZE_PER_PACKET = 18000; protected static final int HEADER_FLAG_FOLLOWING = 0x10000; - + public byte[] read(SocketChannel ch) throws IOException { if (_readHeader) { // Start of a packet if (_readBuffer.position() == 0) { _readBuffer.limit(4); } - + if (ch.read(_readBuffer) == -1) { throw new IOException("Connection closed with -1 on reading size."); } - + if (_readBuffer.hasRemaining()) { s_logger.trace("Need to read the rest of the packet length"); return null; @@ -229,24 +226,24 @@ public class Link { if (s_logger.isTraceEnabled()) { s_logger.trace("Packet length is " + readSize); } - + if (readSize > MAX_SIZE_PER_PACKET) { - throw new IOException("Wrong packet size: " + readSize); + throw new IOException("Wrong packet size: " + readSize); } - + if (!_gotFollowingPacket) { _plaintextBuffer = ByteBuffer.allocate(2000); } - + if ((header & HEADER_FLAG_FOLLOWING) != 0) { _gotFollowingPacket = true; } else { _gotFollowingPacket = false; } - + _readBuffer.clear(); _readHeader = false; - + if (_readBuffer.capacity() < readSize) { if (s_logger.isTraceEnabled()) { s_logger.trace("Resizing the byte buffer from " + _readBuffer.capacity()); @@ -255,18 +252,18 @@ public class Link { } _readBuffer.limit(readSize); } - + if (ch.read(_readBuffer) == -1) { throw new IOException("Connection closed with -1 on read."); } - + if (_readBuffer.hasRemaining()) { // We're not done yet. if (s_logger.isTraceEnabled()) { s_logger.trace("Still has " + _readBuffer.remaining()); } return null; } - + _readBuffer.flip(); ByteBuffer appBuf; @@ -287,7 +284,7 @@ public class Link { if (remaining == _readBuffer.remaining()) { throw new IOException("SSL: Unable to unwrap received data! still remaining " + remaining + "bytes!"); } - + appBuf.flip(); if (_plaintextBuffer.remaining() < appBuf.limit()) { // We need to expand _plaintextBuffer for more data @@ -301,10 +298,10 @@ public class Link { s_logger.trace("Done with packet: " + appBuf.limit()); } } - + _readBuffer.clear(); _readHeader = true; - + if (!_gotFollowingPacket) { _plaintextBuffer.flip(); byte[] result = new byte[_plaintextBuffer.limit()]; @@ -317,15 +314,15 @@ public class Link { return null; } } - + public void send(byte[] data) throws ClosedChannelException { send(data, false); } - + public void send(byte[] data, boolean close) throws ClosedChannelException { send(new ByteBuffer[] { ByteBuffer.wrap(data) }, close); } - + public void send(ByteBuffer[] data, boolean close) throws ClosedChannelException { ByteBuffer[] item = new ByteBuffer[data.length + 1]; int remaining = 0; @@ -333,15 +330,15 @@ public class Link { remaining += data[i].remaining(); item[i + 1] = data[i]; } - + item[0] = ByteBuffer.allocate(4); item[0].putInt(remaining); item[0].flip(); - + if (s_logger.isTraceEnabled()) { s_logger.trace("Sending packet of length " + remaining); } - + _writeQueue.add(item); if (close) { _writeQueue.add(new ByteBuffer[0]); @@ -353,17 +350,17 @@ public class Link { _connection.change(SelectionKey.OP_WRITE, _key, null); } } - + public void send(ByteBuffer[] data) throws ClosedChannelException { send(data, false); } - + public synchronized void close() { if (_key != null) { _connection.close(_key); } } - + public boolean write(SocketChannel ch) throws IOException { ByteBuffer[] data = null; while ((data = _writeQueue.poll()) != null) { @@ -381,26 +378,26 @@ public class Link { } return false; } - + public InetSocketAddress getSocketAddress() { return _addr; } - + public String getIpAddress() { return _addr.getAddress().toString(); } - + public synchronized void terminated() { _key = null; } - + public synchronized void schedule(Task task) throws ClosedChannelException { if (_key == null) { throw new ClosedChannelException(); } _connection.scheduleTask(task); } - + public static SSLContext initSSLContext(boolean isClient) throws Exception { InputStream stream; SSLContext sslContext = null; @@ -408,42 +405,42 @@ public class Link { TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); KeyStore ks = KeyStore.getInstance("JKS"); TrustManager[] tms; - + if (!isClient) { - char[] passphrase = "vmops.com".toCharArray(); - File confFile= PropertiesUtil.findConfigFile("db.properties"); - /* This line may throw a NPE, but that's due to fail to find db.properities, meant some bugs in the other places */ - String confPath = confFile.getParent(); - String keystorePath = confPath + "/cloud.keystore"; - if (new File(keystorePath).exists()) { - stream = new FileInputStream(keystorePath); - } else { - s_logger.warn("SSL: Fail to find the generated keystore. Loading fail-safe one to continue."); - stream = NioConnection.class.getResourceAsStream("/cloud.keystore"); - } - ks.load(stream, passphrase); - stream.close(); - kmf.init(ks, passphrase); - tmf.init(ks); - tms = tmf.getTrustManagers(); + char[] passphrase = "vmops.com".toCharArray(); + File confFile= PropertiesUtil.findConfigFile("db.properties"); + /* This line may throw a NPE, but that's due to fail to find db.properities, meant some bugs in the other places */ + String confPath = confFile.getParent(); + String keystorePath = confPath + "/cloud.keystore"; + if (new File(keystorePath).exists()) { + stream = new FileInputStream(keystorePath); + } else { + s_logger.warn("SSL: Fail to find the generated keystore. Loading fail-safe one to continue."); + stream = NioConnection.class.getResourceAsStream("/cloud.keystore"); + } + ks.load(stream, passphrase); + stream.close(); + kmf.init(ks, passphrase); + tmf.init(ks); + tms = tmf.getTrustManagers(); } else { - ks.load(null, null); - kmf.init(ks, null); - tms = new TrustManager[1]; - tms[0] = new TrustAllManager(); + ks.load(null, null); + kmf.init(ks, null); + tms = new TrustManager[1]; + tms[0] = new TrustAllManager(); } - + sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), tms, null); if (s_logger.isTraceEnabled()) { - s_logger.trace("SSL: SSLcontext has been initialized"); + s_logger.trace("SSL: SSLcontext has been initialized"); } return sslContext; } public static void doHandshake(SocketChannel ch, SSLEngine sslEngine, - boolean isClient) throws IOException { + boolean isClient) throws IOException { if (s_logger.isTraceEnabled()) { s_logger.trace("SSL: begin Handshake, isClient: " + isClient); } @@ -452,13 +449,13 @@ public class Link { SSLSession sslSession = sslEngine.getSession(); HandshakeStatus hsStatus; ByteBuffer in_pkgBuf = - ByteBuffer.allocate(sslSession.getPacketBufferSize() + 40); + ByteBuffer.allocate(sslSession.getPacketBufferSize() + 40); ByteBuffer in_appBuf = - ByteBuffer.allocate(sslSession.getApplicationBufferSize() + 40); + ByteBuffer.allocate(sslSession.getApplicationBufferSize() + 40); ByteBuffer out_pkgBuf = - ByteBuffer.allocate(sslSession.getPacketBufferSize() + 40); + ByteBuffer.allocate(sslSession.getPacketBufferSize() + 40); ByteBuffer out_appBuf = - ByteBuffer.allocate(sslSession.getApplicationBufferSize() + 40); + ByteBuffer.allocate(sslSession.getApplicationBufferSize() + 40); int count; if (isClient) { @@ -498,7 +495,7 @@ public class Link { } engResult = sslEngine.unwrap(in_pkgBuf, in_appBuf); ByteBuffer tmp_pkgBuf = - ByteBuffer.allocate(sslSession.getPacketBufferSize() + 40); + ByteBuffer.allocate(sslSession.getPacketBufferSize() + 40); int loop_count = 0; while (engResult.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) { // The client is too slow? Cut it and let it reconnect @@ -515,13 +512,13 @@ public class Link { throw new IOException("Connection closed with -1 on reading size."); } tmp_pkgBuf.flip(); - + in_pkgBuf.mark(); in_pkgBuf.position(in_pkgBuf.limit()); in_pkgBuf.limit(in_pkgBuf.limit() + tmp_pkgBuf.limit()); in_pkgBuf.put(tmp_pkgBuf); in_pkgBuf.reset(); - + in_appBuf.clear(); engResult = sslEngine.unwrap(in_pkgBuf, in_appBuf); loop_count ++; diff --git a/utils/src/com/cloud/utils/security/CertificateHelper.java b/utils/src/com/cloud/utils/security/CertificateHelper.java index 327734ac133..8344d725bc6 100644 --- a/utils/src/com/cloud/utils/security/CertificateHelper.java +++ b/utils/src/com/cloud/utils/security/CertificateHelper.java @@ -19,7 +19,6 @@ package com.cloud.utils.security; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.security.Key; import java.security.KeyFactory; @@ -38,72 +37,72 @@ import org.apache.commons.codec.binary.Base64; import com.cloud.utils.Ternary; public class CertificateHelper { - public static byte[] buildAndSaveKeystore(String alias, String cert, String privateKey, String storePassword) throws KeyStoreException, CertificateException, - NoSuchAlgorithmException, InvalidKeySpecException, IOException { - KeyStore ks = buildKeystore(alias, cert, privateKey, storePassword); - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - ks.store(os, storePassword != null ? storePassword.toCharArray() : null); - os.close(); - return os.toByteArray(); - } - - public static byte[] buildAndSaveKeystore(List> certs, String storePassword) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, InvalidKeySpecException { - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(null, storePassword != null ? storePassword.toCharArray() : null); + public static byte[] buildAndSaveKeystore(String alias, String cert, String privateKey, String storePassword) throws KeyStoreException, CertificateException, + NoSuchAlgorithmException, InvalidKeySpecException, IOException { + KeyStore ks = buildKeystore(alias, cert, privateKey, storePassword); - //name,cert,key - for (Ternary cert : certs) { - if (cert.third() == null) { - Certificate c = buildCertificate(cert.second()); - ks.setCertificateEntry(cert.first(), c); - } else { - Certificate[] c = new Certificate[certs.size()]; - int i = certs.size(); - for (Ternary ct : certs) { - c[i - 1] = buildCertificate(ct.second()); - i--; - } - ks.setKeyEntry(cert.first(), buildPrivateKey(cert.third()), storePassword != null ? storePassword.toCharArray() : null, c ); - } - } - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - ks.store(os, storePassword != null ? storePassword.toCharArray() : null); - os.close(); - return os.toByteArray(); - } - - public static KeyStore loadKeystore(byte[] ksData, String storePassword) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { - assert(ksData != null); - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(new ByteArrayInputStream(ksData), storePassword != null ? storePassword.toCharArray() : null); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ks.store(os, storePassword != null ? storePassword.toCharArray() : null); + os.close(); + return os.toByteArray(); + } - return ks; - } - - public static KeyStore buildKeystore(String alias, String cert, String privateKey, String storePassword) throws KeyStoreException, CertificateException, - NoSuchAlgorithmException, InvalidKeySpecException, IOException { - - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(null, storePassword != null ? storePassword.toCharArray() : null); - Certificate[] certs = new Certificate[1]; - certs[0] = buildCertificate(cert); - ks.setKeyEntry(alias, buildPrivateKey(privateKey), storePassword != null ? storePassword.toCharArray() : null, certs ); - return ks; - } + public static byte[] buildAndSaveKeystore(List> certs, String storePassword) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, InvalidKeySpecException { + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, storePassword != null ? storePassword.toCharArray() : null); - public static Certificate buildCertificate(String content) throws CertificateException { - assert(content != null); - - BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(content.getBytes())); - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - return cf.generateCertificate(bis); - } + //name,cert,key + for (Ternary cert : certs) { + if (cert.third() == null) { + Certificate c = buildCertificate(cert.second()); + ks.setCertificateEntry(cert.first(), c); + } else { + Certificate[] c = new Certificate[certs.size()]; + int i = certs.size(); + for (Ternary ct : certs) { + c[i - 1] = buildCertificate(ct.second()); + i--; + } + ks.setKeyEntry(cert.first(), buildPrivateKey(cert.third()), storePassword != null ? storePassword.toCharArray() : null, c ); + } + } - public static Key buildPrivateKey(String base64EncodedKeyContent) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { - KeyFactory kf = KeyFactory.getInstance("RSA"); - PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec (Base64.decodeBase64(base64EncodedKeyContent)); - return kf.generatePrivate (keysp); - } + ByteArrayOutputStream os = new ByteArrayOutputStream(); + ks.store(os, storePassword != null ? storePassword.toCharArray() : null); + os.close(); + return os.toByteArray(); + } + + public static KeyStore loadKeystore(byte[] ksData, String storePassword) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { + assert(ksData != null); + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(new ByteArrayInputStream(ksData), storePassword != null ? storePassword.toCharArray() : null); + + return ks; + } + + public static KeyStore buildKeystore(String alias, String cert, String privateKey, String storePassword) throws KeyStoreException, CertificateException, + NoSuchAlgorithmException, InvalidKeySpecException, IOException { + + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, storePassword != null ? storePassword.toCharArray() : null); + Certificate[] certs = new Certificate[1]; + certs[0] = buildCertificate(cert); + ks.setKeyEntry(alias, buildPrivateKey(privateKey), storePassword != null ? storePassword.toCharArray() : null, certs ); + return ks; + } + + public static Certificate buildCertificate(String content) throws CertificateException { + assert(content != null); + + BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(content.getBytes())); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + return cf.generateCertificate(bis); + } + + public static Key buildPrivateKey(String base64EncodedKeyContent) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { + KeyFactory kf = KeyFactory.getInstance("RSA"); + PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec (Base64.decodeBase64(base64EncodedKeyContent)); + return kf.generatePrivate (keysp); + } } From 18bdc58cebdceb4e078edbc8aa28f2a415729cae Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 17:21:58 -0800 Subject: [PATCH 34/73] APIAccessChecker: Refactor and simply plugin implementation using better data structures Signed-off-by: Rohit Yadav --- .../cloudstack/acl/APIAccessChecker.java | 3 +- .../acl/StaticRoleBasedAPIAccessChecker.java | 64 ++++--------------- 2 files changed, 14 insertions(+), 53 deletions(-) diff --git a/api/src/org/apache/cloudstack/acl/APIAccessChecker.java b/api/src/org/apache/cloudstack/acl/APIAccessChecker.java index a5c656d731a..1645fa2c832 100644 --- a/api/src/org/apache/cloudstack/acl/APIAccessChecker.java +++ b/api/src/org/apache/cloudstack/acl/APIAccessChecker.java @@ -17,7 +17,6 @@ package org.apache.cloudstack.acl; import org.apache.cloudstack.acl.RoleType; -import com.cloud.exception.PermissionDeniedException; import com.cloud.utils.component.Adapter; /** @@ -25,5 +24,5 @@ import com.cloud.utils.component.Adapter; */ public interface APIAccessChecker extends Adapter { // Interface for checking access to an API for an user - boolean canAccessAPI(RoleType roleType, String apiCommandName) throws PermissionDeniedException; + boolean canAccessAPI(RoleType roleType, String apiCommandName); } 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 689540aa291..d6bf3f63c74 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,7 +16,6 @@ // under the License. package org.apache.cloudstack.acl; -import com.cloud.exception.PermissionDeniedException; import com.cloud.server.ManagementServer; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.component.ComponentLocator; @@ -39,45 +38,20 @@ import org.apache.log4j.Logger; public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIAccessChecker { protected static final Logger s_logger = Logger.getLogger(StaticRoleBasedAPIAccessChecker.class); - private static Set s_userCommands = null; - private static Set s_resellerCommands = null; // AKA domain-admin - private static Set s_adminCommands = null; - private static Set s_resourceDomainAdminCommands = null; - private static Set s_allCommands = null; + + private static Map> s_roleBasedApisMap = + new HashMap>(); protected StaticRoleBasedAPIAccessChecker() { super(); - s_allCommands = new HashSet(); - s_userCommands = new HashSet(); - s_resellerCommands = new HashSet(); - s_adminCommands = new HashSet(); - s_resourceDomainAdminCommands = new HashSet(); + for (RoleType roleType: RoleType.values()) { + s_roleBasedApisMap.put(roleType, new HashSet()); + } } @Override - public boolean canAccessAPI(RoleType roleType, String commandName) - throws PermissionDeniedException { - - boolean commandExists = s_allCommands.contains(commandName); - boolean commandAccessible = false; - - if (commandExists) { - switch (roleType) { - case Admin: - commandAccessible = s_adminCommands.contains(commandName); - break; - case DomainAdmin: - commandAccessible = s_resellerCommands.contains(commandName); - break; - case ResourceAdmin: - commandAccessible = s_resourceDomainAdminCommands.contains(commandName); - break; - case User: - commandAccessible = s_userCommands.contains(commandName); - break; - } - } - return commandExists && commandAccessible; + public boolean canAccessAPI(RoleType roleType, String commandName) { + return s_roleBasedApisMap.get(roleType).contains(commandName); } @Override @@ -98,31 +72,19 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIA return true; } - private void processConfigFiles(Map config) { - for (Map.Entry entry: config.entrySet()) { + private void processConfigFiles(Map configMap) { + for (Map.Entry entry: configMap.entrySet()) { String apiName = entry.getKey(); String roleMask = entry.getValue(); try { short cmdPermissions = Short.parseShort(roleMask); - if ((cmdPermissions & Admin.getValue()) != 0) { - s_adminCommands.add(apiName); - } - if ((cmdPermissions & ResourceAdmin.getValue()) != 0) { - s_resourceDomainAdminCommands.add(apiName); - } - if ((cmdPermissions & DomainAdmin.getValue()) != 0) { - s_resellerCommands.add(apiName); - } - if ((cmdPermissions & User.getValue()) != 0) { - s_userCommands.add(apiName); + for (RoleType roleType: RoleType.values()) { + if ((cmdPermissions & roleType.getValue()) != 0) + s_roleBasedApisMap.get(roleType).add(apiName); } } catch (NumberFormatException nfe) { s_logger.info("Malformed commands.properties permissions value, for entry: " + entry.toString()); } } - s_allCommands.addAll(s_adminCommands); - s_allCommands.addAll(s_resourceDomainAdminCommands); - s_allCommands.addAll(s_userCommands); - s_allCommands.addAll(s_resellerCommands); } } From 74bb043c37d28bad586ff8a42d8aa6533cef4aa8 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 18:49:18 -0800 Subject: [PATCH 35/73] APIChecker: Rename refactor and add interface checkExistence Signed-off-by: Rohit Yadav --- ...{APIAccessChecker.java => APIChecker.java} | 12 ++++----- client/tomcatconf/components.xml.in | 2 +- .../acl/StaticRoleBasedAPIAccessChecker.java | 16 +++++++++--- server/src/com/cloud/api/ApiServer.java | 25 ++++++++++++------- 4 files changed, 35 insertions(+), 20 deletions(-) rename api/src/org/apache/cloudstack/acl/{APIAccessChecker.java => APIChecker.java} (72%) diff --git a/api/src/org/apache/cloudstack/acl/APIAccessChecker.java b/api/src/org/apache/cloudstack/acl/APIChecker.java similarity index 72% rename from api/src/org/apache/cloudstack/acl/APIAccessChecker.java rename to api/src/org/apache/cloudstack/acl/APIChecker.java index 1645fa2c832..61dd7de75cb 100644 --- a/api/src/org/apache/cloudstack/acl/APIAccessChecker.java +++ b/api/src/org/apache/cloudstack/acl/APIChecker.java @@ -19,10 +19,10 @@ package org.apache.cloudstack.acl; 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(RoleType roleType, String apiCommandName); +// 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/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/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 d6bf3f63c74..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 @@ -29,13 +29,12 @@ import java.util.List; import java.util.Map; import java.util.Set; -import static org.apache.cloudstack.acl.RoleType.*; import org.apache.log4j.Logger; // This is the default API access checker that grab's the user's account // based on the account type, access is granted -@Local(value=APIAccessChecker.class) -public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIAccessChecker { +@Local(value=APIChecker.class) +public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIChecker { protected static final Logger s_logger = Logger.getLogger(StaticRoleBasedAPIAccessChecker.class); @@ -50,10 +49,19 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIA } @Override - public boolean canAccessAPI(RoleType roleType, String commandName) { + public boolean checkAccess(RoleType roleType, String commandName) { return s_roleBasedApisMap.get(roleType).contains(commandName); } + @Override + public boolean checkExistence(String apiName) { + for (RoleType roleType: RoleType.values()) { + if (s_roleBasedApisMap.get(roleType).contains(apiName)) + return true; + } + return false; + } + @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index be3c08716cc..7663e8e724a 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -51,8 +51,7 @@ 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.acl.APIChecker; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.*; import org.apache.cloudstack.api.command.user.account.ListAccountsCmd; @@ -146,8 +145,8 @@ public class ApiServer implements HttpRequestHandler { @Inject private DomainManager _domainMgr = null; @Inject private AsyncJobManager _asyncMgr = null; - @Inject(adapter = APIAccessChecker.class) - protected Adapters _apiAccessCheckers; + @Inject(adapter = APIChecker.class) + protected Adapters _apiAccessCheckers; private Account _systemAccount = null; private User _systemUser = null; @@ -558,7 +557,7 @@ public class ApiServer implements HttpRequestHandler { 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")) { + 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"); } @@ -790,17 +789,25 @@ public class ApiServer implements HttpRequestHandler { return true; } - private boolean isCommandAvailable(User user, String commandName) - throws PermissionDeniedException { + 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) { if (user == null) { return false; } Account account = _accountMgr.getAccount(user.getAccountId()); RoleType roleType = _accountMgr.getRoleType(account); - for (APIAccessChecker apiChecker : _apiAccessCheckers) { + for (APIChecker apiChecker : _apiAccessCheckers) { // Fail the checking if any checker fails to verify - if (!apiChecker.canAccessAPI(roleType, commandName)) + if (!apiChecker.checkAccess(roleType, commandName)) return false; } return true; From 3df026bd51da5f82aa4a4b99fd231ebaa31c6bba Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 18:50:57 -0800 Subject: [PATCH 36/73] ApiDiscoveryServiceImpl: Implement listApis to return response based on role Signed-off-by: Rohit Yadav --- .../discovery/ApiDiscoveryServiceImpl.java | 75 ++++++++++++++----- 1 file changed, 57 insertions(+), 18 deletions(-) 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 2bc17bdbf4d..9f4031c4769 100644 --- a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java +++ b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryServiceImpl.java @@ -16,8 +16,13 @@ // under the License. package org.apache.cloudstack.discovery; -import com.cloud.utils.PropertiesUtil; +import com.cloud.server.ManagementServer; import com.cloud.utils.ReflectUtil; +import com.cloud.utils.component.Adapters; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.component.Inject; +import com.cloud.utils.component.PluggableService; +import org.apache.cloudstack.acl.APIChecker; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.BaseCmd; @@ -42,30 +47,50 @@ 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 _apiNameDiscoveryResponseMap = + new HashMap(); - private Map> _apiNameCmdClassMap = new HashMap>(); + private static Map> _roleTypeDiscoveryResponseListMap = + new HashMap>(); + + private static Map> _apiNameRoleTypeListMap = null; protected ApiDiscoveryServiceImpl() { super(); - generateApiNameCmdClassMap(); + for (RoleType roleType: RoleType.values()) + _roleTypeDiscoveryResponseListMap.put(roleType, new ArrayList()); cacheListApiResponse(); } - private void generateApiNameCmdClassMap() { - 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); + 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 cacheListApiResponse() { + Set> cmdClasses = ReflectUtil.getClassesWithAnnotation(APICommand.class, + new String[]{"org.apache.cloudstack.api", "com.cloud.api"}); - 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); @@ -74,8 +99,9 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { || apiCmdAnnotation.name().isEmpty()) continue; + String apiName = apiCmdAnnotation.name(); ApiDiscoveryResponse response = new ApiDiscoveryResponse(); - response.setName(apiCmdAnnotation.name()); + response.setName(apiName); response.setDescription(apiCmdAnnotation.description()); response.setSince(apiCmdAnnotation.since()); @@ -104,14 +130,27 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { } } response.setObjectName("apis"); - apiDiscoveryResponses.add(response); + _apiNameDiscoveryResponseMap.put(apiName, response); } - _discoveryResponse.setResponses(apiDiscoveryResponses); } @Override public ListResponse listApis(RoleType roleType) { - return _discoveryResponse; + // 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) { + _apiNameRoleTypeListMap = getApiNameRoleTypeListMap(); + for (Map.Entry> entry: _apiNameRoleTypeListMap.entrySet()) { + String apiName = entry.getKey(); + for (RoleType roleTypeInList: entry.getValue()) { + _roleTypeDiscoveryResponseListMap.get(roleTypeInList).add( + _apiNameDiscoveryResponseMap.get(apiName)); + } + } + } + ListResponse response = new ListResponse(); + response.setResponses(_roleTypeDiscoveryResponseListMap.get(roleType)); + return response; } @Override From f1700755586f427702391672e4d487b94813521c Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 22:48:56 -0800 Subject: [PATCH 37/73] ApiDiscovery: Fix response add response class that will hold api response Signed-off-by: Rohit Yadav --- .../api/response/ApiDiscoveryResponse.java | 26 +++++++++-- .../api/response/ApiParameterResponse.java | 11 +++++ .../api/response/ApiResponseResponse.java | 45 +++++++++++++++++++ 3 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiResponseResponse.java diff --git a/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiDiscoveryResponse.java b/plugins/api/discovery/src/org/apache/cloudstack/api/response/ApiDiscoveryResponse.java index 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; + } +} From 86a77e29dc26fee1bb2169a9a1424bdd4e52f65e Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 22:52:10 -0800 Subject: [PATCH 38/73] ApiDiscovery: Fix listApis to return api response, related apis etc. - Fix method to return listApis per api name basis - Return api response, api related cmd etc. as part of response - Caching and processing all cmd, response classes when plugin starts, made class list, maps static so they are shared by multiple instances in case, takes about 1306ms to do the processsing but only on load time - Cache for first listApi() and return precached data thereon, takes 2.2ms for first call, during runtime and 0ms thereon Signed-off-by: Rohit Yadav --- .../apache/cloudstack/api/ApiConstants.java | 1 + .../command/user/discovery/ListApisCmd.java | 22 ++-- .../discovery/ApiDiscoveryService.java | 2 +- .../discovery/ApiDiscoveryServiceImpl.java | 102 +++++++++++++++--- 4 files changed, 103 insertions(+), 24 deletions(-) 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/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 feab20ac5cf..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,12 +16,12 @@ // under the License. package org.apache.cloudstack.api.command.user.discovery; -import com.cloud.user.Account; 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; @@ -30,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"; @@ -39,14 +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) { - Account caller = UserContext.current().getCaller(); RoleType roleType = _accountService.getRoleType(UserContext.current().getCaller()); - ListResponse response = (ListResponse) _apiDiscoveryService.listApis(roleType); + 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); @@ -57,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/discovery/ApiDiscoveryService.java b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java index a1d440e9ccf..611493bfc08 100644 --- a/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java +++ b/plugins/api/discovery/src/org/apache/cloudstack/discovery/ApiDiscoveryService.java @@ -22,5 +22,5 @@ import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.response.ListResponse; public interface ApiDiscoveryService extends PluggableService { - ListResponse listApis(RoleType roleType); + ListResponse 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 9f4031c4769..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,13 +16,13 @@ // 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.component.Adapters; +import com.cloud.utils.StringUtils; import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.component.Inject; import com.cloud.utils.component.PluggableService; -import org.apache.cloudstack.acl.APIChecker; +import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.BaseCmd; @@ -32,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; @@ -39,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; @@ -47,19 +49,24 @@ import java.util.Set; public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { private static final Logger s_logger = Logger.getLogger(ApiDiscoveryServiceImpl.class); + private static Map> _roleTypeDiscoveryResponseListMap; + private static Map _apiNameDiscoveryResponseMap = new HashMap(); - private static Map> _roleTypeDiscoveryResponseListMap = - new HashMap>(); - private static Map> _apiNameRoleTypeListMap = null; protected ApiDiscoveryServiceImpl() { super(); - for (RoleType roleType: RoleType.values()) - _roleTypeDiscoveryResponseListMap.put(roleType, new ArrayList()); - 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 Map> getApiNameRoleTypeListMap() { @@ -86,10 +93,12 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { return apiNameRoleTypeMap; } - private void cacheListApiResponse() { + private void cacheResponseMap() { Set> cmdClasses = ReflectUtil.getClassesWithAnnotation(APICommand.class, new String[]{"org.apache.cloudstack.api", "com.cloud.api"}); + Map> responseApiNameListMap = new HashMap>(); + for(Class cmdClass: cmdClasses) { APICommand apiCmdAnnotation = cmdClass.getAnnotation(APICommand.class); if (apiCmdAnnotation == null) @@ -100,10 +109,32 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { 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(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}); @@ -122,23 +153,50 @@ 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"); + 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); } } @Override - public ListResponse listApis(RoleType roleType) { + public ListResponse 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(); @@ -147,9 +205,21 @@ public class ApiDiscoveryServiceImpl implements ApiDiscoveryService { _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(); - response.setResponses(_roleTypeDiscoveryResponseListMap.get(roleType)); + 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; } From 2bc3b5cc6f3815be29649741ac62b4c8f6cd7016 Mon Sep 17 00:00:00 2001 From: Jessica Tomechak Date: Fri, 11 Jan 2013 00:50:23 -0800 Subject: [PATCH 39/73] Docs. CLOUDSTACK-959. A sub-heading was inadvertently left out of the System Service Offerings section of documentation. Adding the section "Creating a New System Service Offering". --- docs/en-US/system-service-offerings.xml | 1 + 1 file changed, 1 insertion(+) 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. + From 5f19c45e0401bd380e15af26d5c9d1e03d77795d Mon Sep 17 00:00:00 2001 From: Sebastien Goasguen Date: Fri, 11 Jan 2013 10:55:37 +0100 Subject: [PATCH 40/73] Added Marvin building docs in developer guide --- docs/en-US/building-marvin.xml | 46 ++++++++++++++++++++++++++++++++++ docs/en-US/marvin.xml | 1 + 2 files changed, 47 insertions(+) create mode 100644 docs/en-US/building-marvin.xml 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/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 + From 5dd14f322c7332ebb5b9aee22f84209763e891e8 Mon Sep 17 00:00:00 2001 From: Author Name Date: Fri, 11 Jan 2013 15:45:18 +0530 Subject: [PATCH 41/73] --- .../external-guest-firewall-integration.xml | 53 +++--- docs/en-US/external-guest-lb-integration.xml | 4 +- docs/en-US/hardware-firewall.xml | 9 +- docs/en-US/images/add-netscaler.png | Bin 0 -> 22777 bytes docs/en-US/images/parallel-inline-mode.png | Bin 0 -> 145392 bytes docs/en-US/inline-config-lb-fw.xml | 173 ++++++++++++++++++ docs/en-US/lb-services.xml | 25 +++ docs/en-US/management-server-lb.xml | 12 +- docs/en-US/network-setup.xml | 12 +- 9 files changed, 240 insertions(+), 48 deletions(-) create mode 100644 docs/en-US/images/add-netscaler.png create mode 100644 docs/en-US/images/parallel-inline-mode.png create mode 100644 docs/en-US/inline-config-lb-fw.xml create mode 100644 docs/en-US/lb-services.xml diff --git a/docs/en-US/external-guest-firewall-integration.xml b/docs/en-US/external-guest-firewall-integration.xml index 0b34dca1065..bd9ac604970 100644 --- a/docs/en-US/external-guest-firewall-integration.xml +++ b/docs/en-US/external-guest-firewall-integration.xml @@ -21,23 +21,16 @@
External Guest Firewall Integration for Juniper SRX (Optional) - Available only for guests using advanced networking. + Available only for guests using advanced networking, both shared and isolated. &PRODUCT; provides for direct management of the Juniper SRX series of firewalls. This - enables &PRODUCT; to establish static NAT mappings from public IPs to guest VMs, and to use - the Juniper device in place of the virtual router for firewall services. You can have one or - more Juniper SRX per zone. This feature is optional. If Juniper integration is not provisioned, - &PRODUCT; will use the virtual router for these services. + enables &PRODUCT; to establish staticNAT mappings from public IPs to guest VMs, and to use the + Juniper device in place of the virtual router for firewall services. You can have only one + Juniper SRX device per zone. This feature is optional. If Juniper integration is not + provisioned, &PRODUCT; will use the virtual router for these services. The Juniper SRX can optionally be used in conjunction with an external load balancer. - External Network elements can be deployed in a side-by-side or inline configuration. - - - - - - parallel-mode.png: adding a firewall and load balancer in parallel mode. - - + External Network elements can be deployed in a side-by-side or inline configuration. For more + information, see . &PRODUCT; requires the Juniper to be configured as follows: Supported SRX software version is 10.3 or higher. @@ -58,22 +51,22 @@ Record the public and private interface names. If you used a VLAN for the public interface, add a ".[VLAN TAG]" after the interface name. For example, if you are using ge-0/0/3 for your public interface and VLAN tag 301, your public interface name would be - "ge-0/0/3.301". Your private interface name should always be untagged because the - &PRODUCT; software automatically creates tagged logical interfaces. + "ge-0/0/3.301". Your private interface name should always be untagged because the &PRODUCT; + software automatically creates tagged logical interfaces. - Create a public security zone and a private security zone. By default, these will - already exist and will be called "untrust" and "trust". Add the public interface to the - public zone and the private interface to the private zone. Note down the security zone - names. + Create a public security zone and a private security zone. By default, these already + exist and are called "untrust" and "trust" zones. Add the public interface to the public + zone. &PRODUCT;automatically adds the private interface to private zone (trusted zone). Note + down the security zone names. Make sure there is a security policy from the private zone to the public zone that allows all traffic. - Note the username and password of the account you want the &PRODUCT; software to log - in to when it is programming rules. + Note the username and password of the account you want the &PRODUCT; software to log in + to when it is programming rules. Make sure the "ssh" and "xnm-clear-text" system services are enabled. @@ -124,13 +117,13 @@ filter untrust { In the left navigation bar, click Infrastructure. - In Zones, click View More. + In Zones, click View All. Choose the zone you want to work with. - Click the Network tab. + Click the Physical Network tab. In the Network Service Providers node of the diagram, click Configure. (You might have @@ -159,10 +152,6 @@ filter untrust { Private Interface: The name of the private interface on the SRX. For example, ge-0/0/1. - - Usage Interface: (Optional) Typically, the public interface is used to meter - traffic. If you want to use a different interface, specify its name here - Number of Retries: The number of times to attempt a command on the SRX before failing. The default value is 2. @@ -180,12 +169,12 @@ filter untrust { untrust. - Capacity: The number of networks the device can handle + Capacity: The number of networks the device can handle. Dedicated: When marked as dedicated, this device will be dedicated to a single account. When Dedicated is checked, the value in the Capacity field has no significance - implicitly, its value is 1 + implicitly, its value is 1. @@ -194,8 +183,8 @@ filter untrust { Click Global Settings. Set the parameter external.network.stats.interval to indicate how - often you want &PRODUCT; to fetch network usage statistics from the Juniper SRX. If you - are not using the SRX to gather network usage statistics, set to 0. + often you want &PRODUCT; to fetch network usage statistics from the Juniper SRX. If you are + not using the SRX to gather network usage statistics, set to 0.
diff --git a/docs/en-US/external-guest-lb-integration.xml b/docs/en-US/external-guest-lb-integration.xml index 5760f9559e6..acbb514207c 100644 --- a/docs/en-US/external-guest-lb-integration.xml +++ b/docs/en-US/external-guest-lb-integration.xml @@ -20,10 +20,12 @@ -->
External Guest Load Balancer Integration (Optional) + + External load balancer devices are not supported in shared networks. + &PRODUCT; can optionally use a Citrix NetScaler or BigIP F5 load balancer to provide load balancing services to guests. If this is not enabled, &PRODUCT; will use the software load balancer in the virtual router. - To install and enable an external load balancer for &PRODUCT; management: Set up the appliance according to the vendor's directions. diff --git a/docs/en-US/hardware-firewall.xml b/docs/en-US/hardware-firewall.xml index df0568aa2c2..28269cccf31 100644 --- a/docs/en-US/hardware-firewall.xml +++ b/docs/en-US/hardware-firewall.xml @@ -22,8 +22,11 @@ Hardware Firewall All deployments should have a firewall protecting the management server; see Generic Firewall Provisions. Optionally, some deployments may also have a Juniper SRX firewall that will - be the default gateway for the guest networks; see . + be the default gateway for the guest networks; see . - - + + +
diff --git a/docs/en-US/images/add-netscaler.png b/docs/en-US/images/add-netscaler.png new file mode 100644 index 0000000000000000000000000000000000000000..53c1344b9ddd49bebc276af347206ba97948b428 GIT binary patch literal 22777 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 W{~Om+ralt@KQ$%IyX6WNf&T^X{FKQ6 literal 0 HcmV?d00001 diff --git a/docs/en-US/images/parallel-inline-mode.png b/docs/en-US/images/parallel-inline-mode.png new file mode 100644 index 0000000000000000000000000000000000000000..c0c1555365ec7fad20412bbe8d605ea2739c12e0 GIT binary patch literal 145392 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 + +%BOOK_ENTITIES; +]> + +
+ Configuring Network Devices in Inline and Side by Side Modes + The external network elements, such as load balancer and firewall devices, supported in + &PRODUCT; can be deployed in either of the following modes: Side by Side and Inline. Inline mode + was originally supported in &PRODUCT; 2.2.x versions, and is now added back in the 3.0.6 + release. + In Inline mode, one firewall device is placed in front of a load balancing device. The + firewall acts as the gateway for all incoming traffic, then redirect the load balancing traffic + to the load balancer behind it. The load balancer in this case will not have the direct access + to the public network. Deploying network devices in Inline mode ensures that the resources are + protected. + + + + + + parallel-inline-mode.png: external networks in different deployment modes + + + In Side by Side mode, a firewall device is deployed in parallel with the load balancer + device. So the traffic to the load balancer public IP is not routed through the firewall, and + therefore, is exposed to the public network. + + + + + + parallel-mode.png: adding a firewall and load balancer in side by side mode + + + The following table gives you an overview of the supported services and devices for inline + and side by side mode. + + + + + + +
+ + Mode + Firewall + Load Balancer + Supported + + + + + Side by Side + Virtual Router + F5 + Yes + + + Side by Side + Virtual Router + Virtual Router + Yes + + + Side by Side + Virtual Router + NetScaler + Yes + + + Side by Side + Juniper SRX + F5 + Yes + + + Side by Side + Juniper SRX + NetScaler + Yes + + + Inline + Virtual Router + F5 + No + + + Inline + Virtual Router + NetScaler + No + + + Inline + Juniper SRX + F5 + Yes + + + Inline + Juniper SRX + NetScaler + No + + + Inline + Juniper SRX + Virtual Router + No + + + + + To configure SRX and F5 in Inline mode: + + + Configure F5 Big IP and Juniper SRX. + See the respective product documentation for more information. + + + Add SRX and F5 to the same zone in &PRODUCT;. + + Ensure that you select per zone sourceNAT when creating the network offering. When + adding F5 BigIP, do not make it a dedicated device. + + + + Enable both the devices. + + + Create a network offering: + Use SRX as provider for Firewall, Port Forwarding, SourceNAT, and StaticNat. Select F5 + BigIP as the service provider for Load Balancing. Use Virtual Router as the service provider + for DNS, DHCP, user data. + + + Select Inline mode. + For more information, see . + Creating Network Offerings in the Administration Guide. + + + + Start a new VM with this new network offering. + + + Add firewall and load balancing rules. For more information, see + Adding a Load Balancer Rule and . + IP Forwarding and Firewalling in the Administration + Guide. + + + + diff --git a/docs/en-US/lb-services.xml b/docs/en-US/lb-services.xml new file mode 100644 index 00000000000..3bb79dbd335 --- /dev/null +++ b/docs/en-US/lb-services.xml @@ -0,0 +1,25 @@ + + +%BOOK_ENTITIES; +]> + +
+ Load Balancing Services + + +
diff --git a/docs/en-US/management-server-lb.xml b/docs/en-US/management-server-lb.xml index 85a86221c80..f4275786be7 100644 --- a/docs/en-US/management-server-lb.xml +++ b/docs/en-US/management-server-lb.xml @@ -19,12 +19,12 @@ under the License. -->
- Setting Zone VLAN and Running VM Maximums - &PRODUCT; can use a load balancer to provide a virtual IP for multiple Management - Servers. The administrator is responsible for creating the load balancer rules for the - Management Servers. The application requires persistence or stickiness across multiple sessions. - The following chart lists the ports that should be load balanced and whether or not persistence - is required. + Management Server Load Balancing + &PRODUCT; can use a load balancer to provide a virtual IP for multiple Management Servers. + The administrator is responsible for creating the load balancer rules for the Management + Servers. The application requires persistence or stickiness across multiple sessions. The + following chart lists the ports that should be load balanced and whether or not persistence is + required. Even if persistence is not required, enabling it is permitted. diff --git a/docs/en-US/network-setup.xml b/docs/en-US/network-setup.xml index ceee190d4ca..192c8e23d2f 100644 --- a/docs/en-US/network-setup.xml +++ b/docs/en-US/network-setup.xml @@ -20,16 +20,16 @@ --> Network Setup - Achieving the correct networking setup is crucial to a successful &PRODUCT; - installation. This section contains information to help you make decisions and follow the right - procedures to get your network set up correctly. + Achieving the correct networking setup is crucial to a successful &PRODUCT; installation. + This section contains information to help you make decisions and follow the right procedures to + get your network set up correctly. - - + - + From bb59c1e38529ac704eedc0b9ebd759313e60532d Mon Sep 17 00:00:00 2001 From: Pranav Saxena Date: Fri, 11 Jan 2013 15:51:28 +0530 Subject: [PATCH 42/73] Revert "" This reverts commit 5dd14f322c7332ebb5b9aee22f84209763e891e8. --- .../external-guest-firewall-integration.xml | 53 +++--- docs/en-US/external-guest-lb-integration.xml | 4 +- docs/en-US/hardware-firewall.xml | 9 +- docs/en-US/images/add-netscaler.png | Bin 22777 -> 0 bytes docs/en-US/images/parallel-inline-mode.png | Bin 145392 -> 0 bytes docs/en-US/inline-config-lb-fw.xml | 173 ------------------ docs/en-US/lb-services.xml | 25 --- docs/en-US/management-server-lb.xml | 12 +- docs/en-US/network-setup.xml | 12 +- 9 files changed, 48 insertions(+), 240 deletions(-) delete mode 100644 docs/en-US/images/add-netscaler.png delete mode 100644 docs/en-US/images/parallel-inline-mode.png delete mode 100644 docs/en-US/inline-config-lb-fw.xml delete mode 100644 docs/en-US/lb-services.xml diff --git a/docs/en-US/external-guest-firewall-integration.xml b/docs/en-US/external-guest-firewall-integration.xml index bd9ac604970..0b34dca1065 100644 --- a/docs/en-US/external-guest-firewall-integration.xml +++ b/docs/en-US/external-guest-firewall-integration.xml @@ -21,16 +21,23 @@
External Guest Firewall Integration for Juniper SRX (Optional) - Available only for guests using advanced networking, both shared and isolated. + Available only for guests using advanced networking. &PRODUCT; provides for direct management of the Juniper SRX series of firewalls. This - enables &PRODUCT; to establish staticNAT mappings from public IPs to guest VMs, and to use the - Juniper device in place of the virtual router for firewall services. You can have only one - Juniper SRX device per zone. This feature is optional. If Juniper integration is not - provisioned, &PRODUCT; will use the virtual router for these services. + enables &PRODUCT; to establish static NAT mappings from public IPs to guest VMs, and to use + the Juniper device in place of the virtual router for firewall services. You can have one or + more Juniper SRX per zone. This feature is optional. If Juniper integration is not provisioned, + &PRODUCT; will use the virtual router for these services. The Juniper SRX can optionally be used in conjunction with an external load balancer. - External Network elements can be deployed in a side-by-side or inline configuration. For more - information, see . + External Network elements can be deployed in a side-by-side or inline configuration. + + + + + + parallel-mode.png: adding a firewall and load balancer in parallel mode. + + &PRODUCT; requires the Juniper to be configured as follows: Supported SRX software version is 10.3 or higher. @@ -51,22 +58,22 @@ Record the public and private interface names. If you used a VLAN for the public interface, add a ".[VLAN TAG]" after the interface name. For example, if you are using ge-0/0/3 for your public interface and VLAN tag 301, your public interface name would be - "ge-0/0/3.301". Your private interface name should always be untagged because the &PRODUCT; - software automatically creates tagged logical interfaces. + "ge-0/0/3.301". Your private interface name should always be untagged because the + &PRODUCT; software automatically creates tagged logical interfaces. - Create a public security zone and a private security zone. By default, these already - exist and are called "untrust" and "trust" zones. Add the public interface to the public - zone. &PRODUCT;automatically adds the private interface to private zone (trusted zone). Note - down the security zone names. + Create a public security zone and a private security zone. By default, these will + already exist and will be called "untrust" and "trust". Add the public interface to the + public zone and the private interface to the private zone. Note down the security zone + names. Make sure there is a security policy from the private zone to the public zone that allows all traffic. - Note the username and password of the account you want the &PRODUCT; software to log in - to when it is programming rules. + Note the username and password of the account you want the &PRODUCT; software to log + in to when it is programming rules. Make sure the "ssh" and "xnm-clear-text" system services are enabled. @@ -117,13 +124,13 @@ filter untrust { In the left navigation bar, click Infrastructure. - In Zones, click View All. + In Zones, click View More. Choose the zone you want to work with. - Click the Physical Network tab. + Click the Network tab. In the Network Service Providers node of the diagram, click Configure. (You might have @@ -152,6 +159,10 @@ filter untrust { Private Interface: The name of the private interface on the SRX. For example, ge-0/0/1. + + Usage Interface: (Optional) Typically, the public interface is used to meter + traffic. If you want to use a different interface, specify its name here + Number of Retries: The number of times to attempt a command on the SRX before failing. The default value is 2. @@ -169,12 +180,12 @@ filter untrust { untrust. - Capacity: The number of networks the device can handle. + Capacity: The number of networks the device can handle Dedicated: When marked as dedicated, this device will be dedicated to a single account. When Dedicated is checked, the value in the Capacity field has no significance - implicitly, its value is 1. + implicitly, its value is 1 @@ -183,8 +194,8 @@ filter untrust { Click Global Settings. Set the parameter external.network.stats.interval to indicate how - often you want &PRODUCT; to fetch network usage statistics from the Juniper SRX. If you are - not using the SRX to gather network usage statistics, set to 0. + often you want &PRODUCT; to fetch network usage statistics from the Juniper SRX. If you + are not using the SRX to gather network usage statistics, set to 0.
diff --git a/docs/en-US/external-guest-lb-integration.xml b/docs/en-US/external-guest-lb-integration.xml index acbb514207c..5760f9559e6 100644 --- a/docs/en-US/external-guest-lb-integration.xml +++ b/docs/en-US/external-guest-lb-integration.xml @@ -20,12 +20,10 @@ -->
External Guest Load Balancer Integration (Optional) - - External load balancer devices are not supported in shared networks. - &PRODUCT; can optionally use a Citrix NetScaler or BigIP F5 load balancer to provide load balancing services to guests. If this is not enabled, &PRODUCT; will use the software load balancer in the virtual router. + To install and enable an external load balancer for &PRODUCT; management: Set up the appliance according to the vendor's directions. diff --git a/docs/en-US/hardware-firewall.xml b/docs/en-US/hardware-firewall.xml index 28269cccf31..df0568aa2c2 100644 --- a/docs/en-US/hardware-firewall.xml +++ b/docs/en-US/hardware-firewall.xml @@ -22,11 +22,8 @@ Hardware Firewall All deployments should have a firewall protecting the management server; see Generic Firewall Provisions. Optionally, some deployments may also have a Juniper SRX firewall that will - be the default gateway for the guest networks; see . + be the default gateway for the guest networks; see . - - - + +
diff --git a/docs/en-US/images/add-netscaler.png b/docs/en-US/images/add-netscaler.png deleted file mode 100644 index 53c1344b9ddd49bebc276af347206ba97948b428..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22777 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 W{~Om+ralt@KQ$%IyX6WNf&T^X{FKQ6 diff --git a/docs/en-US/images/parallel-inline-mode.png b/docs/en-US/images/parallel-inline-mode.png deleted file mode 100644 index c0c1555365ec7fad20412bbe8d605ea2739c12e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145392 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 - -%BOOK_ENTITIES; -]> - -
- Configuring Network Devices in Inline and Side by Side Modes - The external network elements, such as load balancer and firewall devices, supported in - &PRODUCT; can be deployed in either of the following modes: Side by Side and Inline. Inline mode - was originally supported in &PRODUCT; 2.2.x versions, and is now added back in the 3.0.6 - release. - In Inline mode, one firewall device is placed in front of a load balancing device. The - firewall acts as the gateway for all incoming traffic, then redirect the load balancing traffic - to the load balancer behind it. The load balancer in this case will not have the direct access - to the public network. Deploying network devices in Inline mode ensures that the resources are - protected. - - - - - - parallel-inline-mode.png: external networks in different deployment modes - - - In Side by Side mode, a firewall device is deployed in parallel with the load balancer - device. So the traffic to the load balancer public IP is not routed through the firewall, and - therefore, is exposed to the public network. - - - - - - parallel-mode.png: adding a firewall and load balancer in side by side mode - - - The following table gives you an overview of the supported services and devices for inline - and side by side mode. - - - - - - -
- - Mode - Firewall - Load Balancer - Supported - - - - - Side by Side - Virtual Router - F5 - Yes - - - Side by Side - Virtual Router - Virtual Router - Yes - - - Side by Side - Virtual Router - NetScaler - Yes - - - Side by Side - Juniper SRX - F5 - Yes - - - Side by Side - Juniper SRX - NetScaler - Yes - - - Inline - Virtual Router - F5 - No - - - Inline - Virtual Router - NetScaler - No - - - Inline - Juniper SRX - F5 - Yes - - - Inline - Juniper SRX - NetScaler - No - - - Inline - Juniper SRX - Virtual Router - No - - - - - To configure SRX and F5 in Inline mode: - - - Configure F5 Big IP and Juniper SRX. - See the respective product documentation for more information. - - - Add SRX and F5 to the same zone in &PRODUCT;. - - Ensure that you select per zone sourceNAT when creating the network offering. When - adding F5 BigIP, do not make it a dedicated device. - - - - Enable both the devices. - - - Create a network offering: - Use SRX as provider for Firewall, Port Forwarding, SourceNAT, and StaticNat. Select F5 - BigIP as the service provider for Load Balancing. Use Virtual Router as the service provider - for DNS, DHCP, user data. - - - Select Inline mode. - For more information, see . - Creating Network Offerings in the Administration Guide. - - - - Start a new VM with this new network offering. - - - Add firewall and load balancing rules. For more information, see - Adding a Load Balancer Rule and . - IP Forwarding and Firewalling in the Administration - Guide. - - - - diff --git a/docs/en-US/lb-services.xml b/docs/en-US/lb-services.xml deleted file mode 100644 index 3bb79dbd335..00000000000 --- a/docs/en-US/lb-services.xml +++ /dev/null @@ -1,25 +0,0 @@ - - -%BOOK_ENTITIES; -]> - -
- Load Balancing Services - - -
diff --git a/docs/en-US/management-server-lb.xml b/docs/en-US/management-server-lb.xml index f4275786be7..85a86221c80 100644 --- a/docs/en-US/management-server-lb.xml +++ b/docs/en-US/management-server-lb.xml @@ -19,12 +19,12 @@ under the License. -->
- Management Server Load Balancing - &PRODUCT; can use a load balancer to provide a virtual IP for multiple Management Servers. - The administrator is responsible for creating the load balancer rules for the Management - Servers. The application requires persistence or stickiness across multiple sessions. The - following chart lists the ports that should be load balanced and whether or not persistence is - required. + Setting Zone VLAN and Running VM Maximums + &PRODUCT; can use a load balancer to provide a virtual IP for multiple Management + Servers. The administrator is responsible for creating the load balancer rules for the + Management Servers. The application requires persistence or stickiness across multiple sessions. + The following chart lists the ports that should be load balanced and whether or not persistence + is required. Even if persistence is not required, enabling it is permitted. diff --git a/docs/en-US/network-setup.xml b/docs/en-US/network-setup.xml index 192c8e23d2f..ceee190d4ca 100644 --- a/docs/en-US/network-setup.xml +++ b/docs/en-US/network-setup.xml @@ -20,16 +20,16 @@ --> Network Setup - Achieving the correct networking setup is crucial to a successful &PRODUCT; installation. - This section contains information to help you make decisions and follow the right procedures to - get your network set up correctly. + Achieving the correct networking setup is crucial to a successful &PRODUCT; + installation. This section contains information to help you make decisions and follow the right + procedures to get your network set up correctly. + - + - + From 106730ccdde30450e96d080ed6c9791682fb7300 Mon Sep 17 00:00:00 2001 From: Radhika PC Date: Fri, 11 Jan 2013 15:54:00 +0530 Subject: [PATCH 43/73] SRX and f5 inline mode documentation: Reviewed-By: Jessica Tomechak --- .../external-guest-firewall-integration.xml | 53 +++--- docs/en-US/external-guest-lb-integration.xml | 4 +- docs/en-US/hardware-firewall.xml | 9 +- docs/en-US/images/add-netscaler.png | Bin 0 -> 22777 bytes docs/en-US/images/parallel-inline-mode.png | Bin 0 -> 145392 bytes docs/en-US/inline-config-lb-fw.xml | 173 ++++++++++++++++++ docs/en-US/lb-services.xml | 25 +++ docs/en-US/management-server-lb.xml | 12 +- docs/en-US/network-setup.xml | 12 +- 9 files changed, 240 insertions(+), 48 deletions(-) create mode 100644 docs/en-US/images/add-netscaler.png create mode 100644 docs/en-US/images/parallel-inline-mode.png create mode 100644 docs/en-US/inline-config-lb-fw.xml create mode 100644 docs/en-US/lb-services.xml diff --git a/docs/en-US/external-guest-firewall-integration.xml b/docs/en-US/external-guest-firewall-integration.xml index 0b34dca1065..bd9ac604970 100644 --- a/docs/en-US/external-guest-firewall-integration.xml +++ b/docs/en-US/external-guest-firewall-integration.xml @@ -21,23 +21,16 @@
External Guest Firewall Integration for Juniper SRX (Optional) - Available only for guests using advanced networking. + Available only for guests using advanced networking, both shared and isolated. &PRODUCT; provides for direct management of the Juniper SRX series of firewalls. This - enables &PRODUCT; to establish static NAT mappings from public IPs to guest VMs, and to use - the Juniper device in place of the virtual router for firewall services. You can have one or - more Juniper SRX per zone. This feature is optional. If Juniper integration is not provisioned, - &PRODUCT; will use the virtual router for these services. + enables &PRODUCT; to establish staticNAT mappings from public IPs to guest VMs, and to use the + Juniper device in place of the virtual router for firewall services. You can have only one + Juniper SRX device per zone. This feature is optional. If Juniper integration is not + provisioned, &PRODUCT; will use the virtual router for these services. The Juniper SRX can optionally be used in conjunction with an external load balancer. - External Network elements can be deployed in a side-by-side or inline configuration. - - - - - - parallel-mode.png: adding a firewall and load balancer in parallel mode. - - + External Network elements can be deployed in a side-by-side or inline configuration. For more + information, see . &PRODUCT; requires the Juniper to be configured as follows: Supported SRX software version is 10.3 or higher. @@ -58,22 +51,22 @@ Record the public and private interface names. If you used a VLAN for the public interface, add a ".[VLAN TAG]" after the interface name. For example, if you are using ge-0/0/3 for your public interface and VLAN tag 301, your public interface name would be - "ge-0/0/3.301". Your private interface name should always be untagged because the - &PRODUCT; software automatically creates tagged logical interfaces. + "ge-0/0/3.301". Your private interface name should always be untagged because the &PRODUCT; + software automatically creates tagged logical interfaces. - Create a public security zone and a private security zone. By default, these will - already exist and will be called "untrust" and "trust". Add the public interface to the - public zone and the private interface to the private zone. Note down the security zone - names. + Create a public security zone and a private security zone. By default, these already + exist and are called "untrust" and "trust" zones. Add the public interface to the public + zone. &PRODUCT;automatically adds the private interface to private zone (trusted zone). Note + down the security zone names. Make sure there is a security policy from the private zone to the public zone that allows all traffic. - Note the username and password of the account you want the &PRODUCT; software to log - in to when it is programming rules. + Note the username and password of the account you want the &PRODUCT; software to log in + to when it is programming rules. Make sure the "ssh" and "xnm-clear-text" system services are enabled. @@ -124,13 +117,13 @@ filter untrust { In the left navigation bar, click Infrastructure. - In Zones, click View More. + In Zones, click View All. Choose the zone you want to work with. - Click the Network tab. + Click the Physical Network tab. In the Network Service Providers node of the diagram, click Configure. (You might have @@ -159,10 +152,6 @@ filter untrust { Private Interface: The name of the private interface on the SRX. For example, ge-0/0/1. - - Usage Interface: (Optional) Typically, the public interface is used to meter - traffic. If you want to use a different interface, specify its name here - Number of Retries: The number of times to attempt a command on the SRX before failing. The default value is 2. @@ -180,12 +169,12 @@ filter untrust { untrust. - Capacity: The number of networks the device can handle + Capacity: The number of networks the device can handle. Dedicated: When marked as dedicated, this device will be dedicated to a single account. When Dedicated is checked, the value in the Capacity field has no significance - implicitly, its value is 1 + implicitly, its value is 1. @@ -194,8 +183,8 @@ filter untrust { Click Global Settings. Set the parameter external.network.stats.interval to indicate how - often you want &PRODUCT; to fetch network usage statistics from the Juniper SRX. If you - are not using the SRX to gather network usage statistics, set to 0. + often you want &PRODUCT; to fetch network usage statistics from the Juniper SRX. If you are + not using the SRX to gather network usage statistics, set to 0.
diff --git a/docs/en-US/external-guest-lb-integration.xml b/docs/en-US/external-guest-lb-integration.xml index 5760f9559e6..acbb514207c 100644 --- a/docs/en-US/external-guest-lb-integration.xml +++ b/docs/en-US/external-guest-lb-integration.xml @@ -20,10 +20,12 @@ -->
External Guest Load Balancer Integration (Optional) + + External load balancer devices are not supported in shared networks. + &PRODUCT; can optionally use a Citrix NetScaler or BigIP F5 load balancer to provide load balancing services to guests. If this is not enabled, &PRODUCT; will use the software load balancer in the virtual router. - To install and enable an external load balancer for &PRODUCT; management: Set up the appliance according to the vendor's directions. diff --git a/docs/en-US/hardware-firewall.xml b/docs/en-US/hardware-firewall.xml index df0568aa2c2..28269cccf31 100644 --- a/docs/en-US/hardware-firewall.xml +++ b/docs/en-US/hardware-firewall.xml @@ -22,8 +22,11 @@ Hardware Firewall All deployments should have a firewall protecting the management server; see Generic Firewall Provisions. Optionally, some deployments may also have a Juniper SRX firewall that will - be the default gateway for the guest networks; see . + be the default gateway for the guest networks; see . - - + + +
diff --git a/docs/en-US/images/add-netscaler.png b/docs/en-US/images/add-netscaler.png new file mode 100644 index 0000000000000000000000000000000000000000..53c1344b9ddd49bebc276af347206ba97948b428 GIT binary patch literal 22777 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 W{~Om+ralt@KQ$%IyX6WNf&T^X{FKQ6 literal 0 HcmV?d00001 diff --git a/docs/en-US/images/parallel-inline-mode.png b/docs/en-US/images/parallel-inline-mode.png new file mode 100644 index 0000000000000000000000000000000000000000..c0c1555365ec7fad20412bbe8d605ea2739c12e0 GIT binary patch literal 145392 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 + +%BOOK_ENTITIES; +]> + +
+ Configuring Network Devices in Inline and Side by Side Modes + The external network elements, such as load balancer and firewall devices, supported in + &PRODUCT; can be deployed in either of the following modes: Side by Side and Inline. Inline mode + was originally supported in &PRODUCT; 2.2.x versions, and is now added back in the 3.0.6 + release. + In Inline mode, one firewall device is placed in front of a load balancing device. The + firewall acts as the gateway for all incoming traffic, then redirect the load balancing traffic + to the load balancer behind it. The load balancer in this case will not have the direct access + to the public network. Deploying network devices in Inline mode ensures that the resources are + protected. + + + + + + parallel-inline-mode.png: external networks in different deployment modes + + + In Side by Side mode, a firewall device is deployed in parallel with the load balancer + device. So the traffic to the load balancer public IP is not routed through the firewall, and + therefore, is exposed to the public network. + + + + + + parallel-mode.png: adding a firewall and load balancer in side by side mode + + + The following table gives you an overview of the supported services and devices for inline + and side by side mode. + + + + + + +
+ + Mode + Firewall + Load Balancer + Supported + + + + + Side by Side + Virtual Router + F5 + Yes + + + Side by Side + Virtual Router + Virtual Router + Yes + + + Side by Side + Virtual Router + NetScaler + Yes + + + Side by Side + Juniper SRX + F5 + Yes + + + Side by Side + Juniper SRX + NetScaler + Yes + + + Inline + Virtual Router + F5 + No + + + Inline + Virtual Router + NetScaler + No + + + Inline + Juniper SRX + F5 + Yes + + + Inline + Juniper SRX + NetScaler + No + + + Inline + Juniper SRX + Virtual Router + No + + + + + To configure SRX and F5 in Inline mode: + + + Configure F5 Big IP and Juniper SRX. + See the respective product documentation for more information. + + + Add SRX and F5 to the same zone in &PRODUCT;. + + Ensure that you select per zone sourceNAT when creating the network offering. When + adding F5 BigIP, do not make it a dedicated device. + + + + Enable both the devices. + + + Create a network offering: + Use SRX as provider for Firewall, Port Forwarding, SourceNAT, and StaticNat. Select F5 + BigIP as the service provider for Load Balancing. Use Virtual Router as the service provider + for DNS, DHCP, user data. + + + Select Inline mode. + For more information, see . + Creating Network Offerings in the Administration Guide. + + + + Start a new VM with this new network offering. + + + Add firewall and load balancing rules. For more information, see + Adding a Load Balancer Rule and . + IP Forwarding and Firewalling in the Administration + Guide. + + + + diff --git a/docs/en-US/lb-services.xml b/docs/en-US/lb-services.xml new file mode 100644 index 00000000000..3bb79dbd335 --- /dev/null +++ b/docs/en-US/lb-services.xml @@ -0,0 +1,25 @@ + + +%BOOK_ENTITIES; +]> + +
+ Load Balancing Services + + +
diff --git a/docs/en-US/management-server-lb.xml b/docs/en-US/management-server-lb.xml index 85a86221c80..f4275786be7 100644 --- a/docs/en-US/management-server-lb.xml +++ b/docs/en-US/management-server-lb.xml @@ -19,12 +19,12 @@ under the License. -->
- Setting Zone VLAN and Running VM Maximums - &PRODUCT; can use a load balancer to provide a virtual IP for multiple Management - Servers. The administrator is responsible for creating the load balancer rules for the - Management Servers. The application requires persistence or stickiness across multiple sessions. - The following chart lists the ports that should be load balanced and whether or not persistence - is required. + Management Server Load Balancing + &PRODUCT; can use a load balancer to provide a virtual IP for multiple Management Servers. + The administrator is responsible for creating the load balancer rules for the Management + Servers. The application requires persistence or stickiness across multiple sessions. The + following chart lists the ports that should be load balanced and whether or not persistence is + required. Even if persistence is not required, enabling it is permitted. diff --git a/docs/en-US/network-setup.xml b/docs/en-US/network-setup.xml index ceee190d4ca..192c8e23d2f 100644 --- a/docs/en-US/network-setup.xml +++ b/docs/en-US/network-setup.xml @@ -20,16 +20,16 @@ --> Network Setup - Achieving the correct networking setup is crucial to a successful &PRODUCT; - installation. This section contains information to help you make decisions and follow the right - procedures to get your network set up correctly. + Achieving the correct networking setup is crucial to a successful &PRODUCT; installation. + This section contains information to help you make decisions and follow the right procedures to + get your network set up correctly. - - + - + From eb40d2337e0ae10876a27dfbc22575be8e9d593d Mon Sep 17 00:00:00 2001 From: Prasanna Santhanam Date: Fri, 11 Jan 2013 17:03:24 +0530 Subject: [PATCH 44/73] apidoc: fixing the api doc failure remove api-discovery_commands.properties since the plugin returns the listApis call as a map directly. not needed for api doc generation. Signed-off-by: Prasanna Santhanam --- tools/apidoc/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 499c474ed0123865f3f4feca5f6320ac322b6f37 Mon Sep 17 00:00:00 2001 From: Radhika PC Date: Fri, 11 Jan 2013 17:53:09 +0530 Subject: [PATCH 45/73] Autoscale Documentation : Reviewed-By: Jessica Tomechak --- docs/en-US/autoscale.xml | 284 ++++++++++++++++++ docs/en-US/configure-snmp-rhel.xml | 86 ++++++ .../external-firewalls-and-load-balancers.xml | 43 +-- ...ion-of-external-firewalls-loadbalancer.xml | 46 +++ 4 files changed, 440 insertions(+), 19 deletions(-) create mode 100644 docs/en-US/autoscale.xml create mode 100644 docs/en-US/configure-snmp-rhel.xml create mode 100644 docs/en-US/ongoing-configuration-of-external-firewalls-loadbalancer.xml 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/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; +]> + +
+ Configuring SNMP Community String on a RHEL Server + The SNMP Community string is similar to a user id or password that provides access to a + network device, such as router. This string is sent along with all SNMP requests. If the + community string is correct, the device responds with the requested information. If the + community string is incorrect, the device discards the request and does not respond. + The NetScaler device uses SNMP to communicate with the VMs. You must install SNMP and + configure SNMP Community string for a secure communication between the NetScaler device and the + RHEL machine. + + + Ensure that you installed SNMP on RedHat. If not, run the following command: + yum install net-snmp-utils + + + Edit the /etc/snmp/snmpd.conf file to allow the SNMP polling from the NetScaler + device. + + + Map the community name into a security name (local and mynetwork, depending on where + the request is coming from): + + Use a strong password instead of public when you edit the following table. + + # sec.name source community +com2sec local localhost public +com2sec mynetwork 0.0.0.0 public + + Setting to 0.0.0.0 allows all IPs to poll the NetScaler server. + + + + Map the security names into group names: + # group.name sec.model sec.name +group MyRWGroup v1 local +group MyRWGroup v2c local +group MyROGroup v1 mynetwork +group MyROGroup v2c mynetwork + + + Create a view to allow the groups to have the permission to: + incl/excl subtree mask view all included .1 + + + Grant access with different write permissions to the two groups to the view you + created. + # context sec.model sec.level prefix read write notif + access MyROGroup "" any noauth exact all none none + access MyRWGroup "" any noauth exact all all all + + + + + Unblock SNMP in iptables. + iptables -A INPUT -p udp --dport 161 -j ACCEPT + + + Start the SNMP service: + service snmpd start + + + Ensure that the SNMP service is started automatically during the system startup: + chkconfig snmpd on + + +
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/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. +
From 2b3084bba09f631d93c233238b80756b7395f938 Mon Sep 17 00:00:00 2001 From: Chip Childers Date: Fri, 11 Jan 2013 10:19:38 -0500 Subject: [PATCH 46/73] Per my veto vote on the dev list, reverting "SRX and f5 inline mode documentation: Reviewed-By: Jessica Tomechak" This reverts commit 106730ccdde30450e96d080ed6c9791682fb7300. --- .../external-guest-firewall-integration.xml | 53 +++--- docs/en-US/external-guest-lb-integration.xml | 4 +- docs/en-US/hardware-firewall.xml | 9 +- docs/en-US/images/add-netscaler.png | Bin 22777 -> 0 bytes docs/en-US/images/parallel-inline-mode.png | Bin 145392 -> 0 bytes docs/en-US/inline-config-lb-fw.xml | 173 ------------------ docs/en-US/lb-services.xml | 25 --- docs/en-US/management-server-lb.xml | 12 +- docs/en-US/network-setup.xml | 12 +- 9 files changed, 48 insertions(+), 240 deletions(-) delete mode 100644 docs/en-US/images/add-netscaler.png delete mode 100644 docs/en-US/images/parallel-inline-mode.png delete mode 100644 docs/en-US/inline-config-lb-fw.xml delete mode 100644 docs/en-US/lb-services.xml diff --git a/docs/en-US/external-guest-firewall-integration.xml b/docs/en-US/external-guest-firewall-integration.xml index bd9ac604970..0b34dca1065 100644 --- a/docs/en-US/external-guest-firewall-integration.xml +++ b/docs/en-US/external-guest-firewall-integration.xml @@ -21,16 +21,23 @@
External Guest Firewall Integration for Juniper SRX (Optional) - Available only for guests using advanced networking, both shared and isolated. + Available only for guests using advanced networking. &PRODUCT; provides for direct management of the Juniper SRX series of firewalls. This - enables &PRODUCT; to establish staticNAT mappings from public IPs to guest VMs, and to use the - Juniper device in place of the virtual router for firewall services. You can have only one - Juniper SRX device per zone. This feature is optional. If Juniper integration is not - provisioned, &PRODUCT; will use the virtual router for these services. + enables &PRODUCT; to establish static NAT mappings from public IPs to guest VMs, and to use + the Juniper device in place of the virtual router for firewall services. You can have one or + more Juniper SRX per zone. This feature is optional. If Juniper integration is not provisioned, + &PRODUCT; will use the virtual router for these services. The Juniper SRX can optionally be used in conjunction with an external load balancer. - External Network elements can be deployed in a side-by-side or inline configuration. For more - information, see . + External Network elements can be deployed in a side-by-side or inline configuration. + + + + + + parallel-mode.png: adding a firewall and load balancer in parallel mode. + + &PRODUCT; requires the Juniper to be configured as follows: Supported SRX software version is 10.3 or higher. @@ -51,22 +58,22 @@ Record the public and private interface names. If you used a VLAN for the public interface, add a ".[VLAN TAG]" after the interface name. For example, if you are using ge-0/0/3 for your public interface and VLAN tag 301, your public interface name would be - "ge-0/0/3.301". Your private interface name should always be untagged because the &PRODUCT; - software automatically creates tagged logical interfaces. + "ge-0/0/3.301". Your private interface name should always be untagged because the + &PRODUCT; software automatically creates tagged logical interfaces. - Create a public security zone and a private security zone. By default, these already - exist and are called "untrust" and "trust" zones. Add the public interface to the public - zone. &PRODUCT;automatically adds the private interface to private zone (trusted zone). Note - down the security zone names. + Create a public security zone and a private security zone. By default, these will + already exist and will be called "untrust" and "trust". Add the public interface to the + public zone and the private interface to the private zone. Note down the security zone + names. Make sure there is a security policy from the private zone to the public zone that allows all traffic. - Note the username and password of the account you want the &PRODUCT; software to log in - to when it is programming rules. + Note the username and password of the account you want the &PRODUCT; software to log + in to when it is programming rules. Make sure the "ssh" and "xnm-clear-text" system services are enabled. @@ -117,13 +124,13 @@ filter untrust { In the left navigation bar, click Infrastructure. - In Zones, click View All. + In Zones, click View More. Choose the zone you want to work with. - Click the Physical Network tab. + Click the Network tab. In the Network Service Providers node of the diagram, click Configure. (You might have @@ -152,6 +159,10 @@ filter untrust { Private Interface: The name of the private interface on the SRX. For example, ge-0/0/1. + + Usage Interface: (Optional) Typically, the public interface is used to meter + traffic. If you want to use a different interface, specify its name here + Number of Retries: The number of times to attempt a command on the SRX before failing. The default value is 2. @@ -169,12 +180,12 @@ filter untrust { untrust. - Capacity: The number of networks the device can handle. + Capacity: The number of networks the device can handle Dedicated: When marked as dedicated, this device will be dedicated to a single account. When Dedicated is checked, the value in the Capacity field has no significance - implicitly, its value is 1. + implicitly, its value is 1 @@ -183,8 +194,8 @@ filter untrust { Click Global Settings. Set the parameter external.network.stats.interval to indicate how - often you want &PRODUCT; to fetch network usage statistics from the Juniper SRX. If you are - not using the SRX to gather network usage statistics, set to 0. + often you want &PRODUCT; to fetch network usage statistics from the Juniper SRX. If you + are not using the SRX to gather network usage statistics, set to 0.
diff --git a/docs/en-US/external-guest-lb-integration.xml b/docs/en-US/external-guest-lb-integration.xml index acbb514207c..5760f9559e6 100644 --- a/docs/en-US/external-guest-lb-integration.xml +++ b/docs/en-US/external-guest-lb-integration.xml @@ -20,12 +20,10 @@ -->
External Guest Load Balancer Integration (Optional) - - External load balancer devices are not supported in shared networks. - &PRODUCT; can optionally use a Citrix NetScaler or BigIP F5 load balancer to provide load balancing services to guests. If this is not enabled, &PRODUCT; will use the software load balancer in the virtual router. + To install and enable an external load balancer for &PRODUCT; management: Set up the appliance according to the vendor's directions. diff --git a/docs/en-US/hardware-firewall.xml b/docs/en-US/hardware-firewall.xml index 28269cccf31..df0568aa2c2 100644 --- a/docs/en-US/hardware-firewall.xml +++ b/docs/en-US/hardware-firewall.xml @@ -22,11 +22,8 @@ Hardware Firewall All deployments should have a firewall protecting the management server; see Generic Firewall Provisions. Optionally, some deployments may also have a Juniper SRX firewall that will - be the default gateway for the guest networks; see . + be the default gateway for the guest networks; see . - - - + +
diff --git a/docs/en-US/images/add-netscaler.png b/docs/en-US/images/add-netscaler.png deleted file mode 100644 index 53c1344b9ddd49bebc276af347206ba97948b428..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22777 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 W{~Om+ralt@KQ$%IyX6WNf&T^X{FKQ6 diff --git a/docs/en-US/images/parallel-inline-mode.png b/docs/en-US/images/parallel-inline-mode.png deleted file mode 100644 index c0c1555365ec7fad20412bbe8d605ea2739c12e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145392 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 - -%BOOK_ENTITIES; -]> - -
- Configuring Network Devices in Inline and Side by Side Modes - The external network elements, such as load balancer and firewall devices, supported in - &PRODUCT; can be deployed in either of the following modes: Side by Side and Inline. Inline mode - was originally supported in &PRODUCT; 2.2.x versions, and is now added back in the 3.0.6 - release. - In Inline mode, one firewall device is placed in front of a load balancing device. The - firewall acts as the gateway for all incoming traffic, then redirect the load balancing traffic - to the load balancer behind it. The load balancer in this case will not have the direct access - to the public network. Deploying network devices in Inline mode ensures that the resources are - protected. - - - - - - parallel-inline-mode.png: external networks in different deployment modes - - - In Side by Side mode, a firewall device is deployed in parallel with the load balancer - device. So the traffic to the load balancer public IP is not routed through the firewall, and - therefore, is exposed to the public network. - - - - - - parallel-mode.png: adding a firewall and load balancer in side by side mode - - - The following table gives you an overview of the supported services and devices for inline - and side by side mode. - - - - - - -
- - Mode - Firewall - Load Balancer - Supported - - - - - Side by Side - Virtual Router - F5 - Yes - - - Side by Side - Virtual Router - Virtual Router - Yes - - - Side by Side - Virtual Router - NetScaler - Yes - - - Side by Side - Juniper SRX - F5 - Yes - - - Side by Side - Juniper SRX - NetScaler - Yes - - - Inline - Virtual Router - F5 - No - - - Inline - Virtual Router - NetScaler - No - - - Inline - Juniper SRX - F5 - Yes - - - Inline - Juniper SRX - NetScaler - No - - - Inline - Juniper SRX - Virtual Router - No - - - - - To configure SRX and F5 in Inline mode: - - - Configure F5 Big IP and Juniper SRX. - See the respective product documentation for more information. - - - Add SRX and F5 to the same zone in &PRODUCT;. - - Ensure that you select per zone sourceNAT when creating the network offering. When - adding F5 BigIP, do not make it a dedicated device. - - - - Enable both the devices. - - - Create a network offering: - Use SRX as provider for Firewall, Port Forwarding, SourceNAT, and StaticNat. Select F5 - BigIP as the service provider for Load Balancing. Use Virtual Router as the service provider - for DNS, DHCP, user data. - - - Select Inline mode. - For more information, see . - Creating Network Offerings in the Administration Guide. - - - - Start a new VM with this new network offering. - - - Add firewall and load balancing rules. For more information, see - Adding a Load Balancer Rule and . - IP Forwarding and Firewalling in the Administration - Guide. - - - - diff --git a/docs/en-US/lb-services.xml b/docs/en-US/lb-services.xml deleted file mode 100644 index 3bb79dbd335..00000000000 --- a/docs/en-US/lb-services.xml +++ /dev/null @@ -1,25 +0,0 @@ - - -%BOOK_ENTITIES; -]> - -
- Load Balancing Services - - -
diff --git a/docs/en-US/management-server-lb.xml b/docs/en-US/management-server-lb.xml index f4275786be7..85a86221c80 100644 --- a/docs/en-US/management-server-lb.xml +++ b/docs/en-US/management-server-lb.xml @@ -19,12 +19,12 @@ under the License. -->
- Management Server Load Balancing - &PRODUCT; can use a load balancer to provide a virtual IP for multiple Management Servers. - The administrator is responsible for creating the load balancer rules for the Management - Servers. The application requires persistence or stickiness across multiple sessions. The - following chart lists the ports that should be load balanced and whether or not persistence is - required. + Setting Zone VLAN and Running VM Maximums + &PRODUCT; can use a load balancer to provide a virtual IP for multiple Management + Servers. The administrator is responsible for creating the load balancer rules for the + Management Servers. The application requires persistence or stickiness across multiple sessions. + The following chart lists the ports that should be load balanced and whether or not persistence + is required. Even if persistence is not required, enabling it is permitted. diff --git a/docs/en-US/network-setup.xml b/docs/en-US/network-setup.xml index 192c8e23d2f..ceee190d4ca 100644 --- a/docs/en-US/network-setup.xml +++ b/docs/en-US/network-setup.xml @@ -20,16 +20,16 @@ --> Network Setup - Achieving the correct networking setup is crucial to a successful &PRODUCT; installation. - This section contains information to help you make decisions and follow the right procedures to - get your network set up correctly. + Achieving the correct networking setup is crucial to a successful &PRODUCT; + installation. This section contains information to help you make decisions and follow the right + procedures to get your network set up correctly. + - + - + From 66514c00efad5c93a2b0996f1e79222cbff9bd48 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 11 Jan 2013 09:45:04 -0800 Subject: [PATCH 47/73] apidoc: Remove api discovery from toc Based on eb40d2337e0ae10876a27dfbc22575be8e9d593d Signed-off-by: Rohit Yadav --- tools/apidoc/gen_toc.py | 1 - 1 file changed, 1 deletion(-) 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', } From d8ebd5e4f9d96e8abb7a64c728ac8d8445aac3a4 Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Fri, 11 Jan 2013 11:52:46 -0700 Subject: [PATCH 48/73] Summary: Change url of tiny linux in devcloud-kvm.sql Signed-off-by: Marcus Sorensen 1357930366 -0700 --- tools/devcloud-kvm/devcloud-kvm.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/devcloud-kvm/devcloud-kvm.sql b/tools/devcloud-kvm/devcloud-kvm.sql index eeba64153a3..97478834bf3 100644 --- a/tools/devcloud-kvm/devcloud-kvm.sql +++ b/tools/devcloud-kvm/devcloud-kvm.sql @@ -37,4 +37,4 @@ INSERT INTO `cloud`.`configuration` (instance, name, value) VALUE('DEFAULT', 'se 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="https://dl.dropbox.com/u/678991/cloudstack-extras/ttylinux_pv.qcow2",checksum="81dcf4b4ca05a3b637a040e851568f29",display_text="tiny Linux",format='QCOW2',hypervisor_type='KVM' where id=5; +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; From 1033200b0b5943cc21d5397d12d3b54b69aa2273 Mon Sep 17 00:00:00 2001 From: Noa Resare Date: Wed, 9 Jan 2013 16:39:10 +0100 Subject: [PATCH 49/73] CLOUDSTACK-933: CglibThrowableRendererTest writing stack traces... Improve CglibThrowableRenderer test case Log to a separate Logger instead of the default one to avoid spurious stack traces in test run output. Actually verify that registering CglibThrowableRenderer with the alternative log hierarchy actually removes call trace lines that contains the string --- .../utils/log/CglibThrowableRendererTest.java | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) 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("")); + } } From 53da542001592a7457e03856d8db4775e6a9c7f1 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Thu, 10 Jan 2013 17:08:17 -0800 Subject: [PATCH 50/73] Another round of fixes after merge --- .../storage/image/db/ImageDataDaoImpl.java | 14 +++++----- .../datastore/db/PrimaryDataStoreDaoImpl.java | 7 ++--- server/src/com/cloud/api/ApiServer.java | 8 +++--- .../com/cloud/api/query/QueryManagerImpl.java | 2 ++ .../cloud/async/dao/SyncQueueItemDaoImpl.java | 2 ++ .../baremetal/BareMetalVmManagerImpl.java | 6 +++- server/src/com/cloud/event/EventUtils.java | 5 ++++ .../cloud/server/ManagementServerImpl.java | 28 +++++++++---------- 8 files changed, 42 insertions(+), 30 deletions(-) diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/db/ImageDataDaoImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/db/ImageDataDaoImpl.java index f710a2d6ab7..4c37c9d58e6 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/image/db/ImageDataDaoImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/image/db/ImageDataDaoImpl.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.storage.image.format.ISO; @@ -73,18 +74,18 @@ public class ImageDataDaoImpl extends GenericDaoBase implemen private static final Logger s_logger = Logger.getLogger(VMTemplateDaoImpl.class); - VMTemplateZoneDao _templateZoneDao = null; + @Inject VMTemplateZoneDao _templateZoneDao = null; - VMTemplateDetailsDao _templateDetailsDao = null; + @Inject VMTemplateDetailsDao _templateDetailsDao = null; - ConfigurationDao _configDao = null; + @Inject ConfigurationDao _configDao = null; - HostDao _hostDao = null; + @Inject HostDao _hostDao = null; - DomainDao _domainDao = null; + @Inject DomainDao _domainDao = null; - DataCenterDao _dcDao = null; + @Inject DataCenterDao _dcDao = null; private final String SELECT_TEMPLATE_HOST_REF = "SELECT t.id, h.data_center_id, t.unique_name, t.name, t.public, t.featured, t.type, t.hvm, t.bits, t.url, t.format, t.created, t.account_id, " + "t.checksum, t.display_text, t.enable_password, t.guest_os_id, t.bootable, t.prepopulate, t.cross_zones, t.hypervisor_type FROM vm_template t"; @@ -923,5 +924,4 @@ public class ImageDataDaoImpl extends GenericDaoBase implemen private boolean isAdmin(short accountType) { return ((accountType == Account.ACCOUNT_TYPE_ADMIN) || (accountType == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) || (accountType == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) || (accountType == Account.ACCOUNT_TYPE_READ_ONLY_ADMIN)); } - } \ No newline at end of file diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java index 99cb00140ec..ef42208ec51 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.storage.datastore.DataStoreStatus; @@ -49,7 +50,7 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase DeleteLvmSearch; protected final GenericSearchBuilder StatusCountSearch; - protected final PrimaryDataStoreDetailsDao _detailsDao = null; + @Inject protected PrimaryDataStoreDetailsDao _detailsDao; private final String DetailsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_details ON storage_pool.id = storage_pool_details.pool_id WHERE storage_pool.removed is null and storage_pool.data_center_id = ? and (storage_pool.pod_id = ? or storage_pool.pod_id is null) and ("; private final String DetailsSqlSuffix = ") GROUP BY storage_pool_details.pool_id HAVING COUNT(storage_pool_details.name) >= ?"; @@ -95,9 +96,7 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase findPoolByName(String name) { diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 0c64e588a40..b4c0e2760f8 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -147,10 +147,10 @@ public class ApiServer implements HttpRequestHandler { public static String jsonContentType = "text/javascript"; @Inject ApiDispatcher _dispatcher; - @Inject AccountManager _accountMgr; - @Inject DomainManager _domainMgr; - @Inject AsyncJobManager _asyncMgr; - @Inject ConfigurationDao _configDao; + @Inject private AccountManager _accountMgr; + @Inject private DomainManager _domainMgr = null; + @Inject private AsyncJobManager _asyncMgr = null; + @Inject private ConfigurationDao _configDao; @Inject List _pluggableServices; @Inject IdentityDao _identityDao; diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index 4e2355e9058..116e7113066 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -58,6 +58,7 @@ import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.query.QueryService; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.api.query.dao.AccountJoinDao; import com.cloud.api.query.dao.AsyncJobJoinDao; @@ -128,6 +129,7 @@ import com.cloud.vm.dao.UserVmDao; * @author minc * */ +@Component @Local(value = {QueryService.class }) public class QueryManagerImpl implements QueryService, Manager { diff --git a/server/src/com/cloud/async/dao/SyncQueueItemDaoImpl.java b/server/src/com/cloud/async/dao/SyncQueueItemDaoImpl.java index 8ee21f39af5..d2d292976d8 100644 --- a/server/src/com/cloud/async/dao/SyncQueueItemDaoImpl.java +++ b/server/src/com/cloud/async/dao/SyncQueueItemDaoImpl.java @@ -33,6 +33,7 @@ import org.springframework.stereotype.Component; import com.cloud.async.SyncQueueItemVO; import com.cloud.utils.DateUtil; +import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; @@ -43,6 +44,7 @@ import com.cloud.utils.db.Transaction; @Component @Local(value = { SyncQueueItemDao.class }) +@DB public class SyncQueueItemDaoImpl extends GenericDaoBase implements SyncQueueItemDao { private static final Logger s_logger = Logger.getLogger(SyncQueueItemDaoImpl.class); final GenericSearchBuilder queueIdSearch; diff --git a/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java index 54c276dccbd..35983fa90be 100755 --- a/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java +++ b/server/src/com/cloud/baremetal/BareMetalVmManagerImpl.java @@ -40,6 +40,9 @@ import com.cloud.agent.api.baremetal.IpmISetBootDevCommand; import com.cloud.agent.api.baremetal.IpmiBootorResetCommand; import com.cloud.agent.manager.Commands; import org.apache.cloudstack.api.command.user.vm.StartVMCmd; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + import com.cloud.baremetal.PxeServerManager.PxeServerType; import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.dao.ConfigurationDao; @@ -102,6 +105,8 @@ import com.cloud.vm.VirtualMachineName; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VirtualMachineProfile.Param; +@Component +@Primary @Local(value={BareMetalVmManager.class, BareMetalVmService.class}) public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMetalVmManager, BareMetalVmService, Manager, StateListener { @@ -110,7 +115,6 @@ public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMet @Inject PxeServerManager _pxeMgr; @Inject ResourceManager _resourceMgr; - // @com.cloud.utils.component.Inject (adapter=TemplateAdapter.class) @Inject protected List _adapters; @PostConstruct diff --git a/server/src/com/cloud/event/EventUtils.java b/server/src/com/cloud/event/EventUtils.java index 68317bf32d6..53d224e186f 100755 --- a/server/src/com/cloud/event/EventUtils.java +++ b/server/src/com/cloud/event/EventUtils.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.event; +import javax.annotation.PostConstruct; import javax.inject.Inject; import org.springframework.stereotype.Component; @@ -33,6 +34,10 @@ public class EventUtils { @Inject AccountDao _placeHoderAccountDao; public EventUtils() { + } + + @PostConstruct + void init() { _eventDao = _placeHoderEventDao; _accountDao = _placeHoderAccountDao; } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index d8525d63c7f..c9a59040216 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -58,7 +58,6 @@ import org.apache.cloudstack.api.command.admin.pod.ListPodsByCmd; import org.apache.cloudstack.api.command.admin.resource.ListAlertsCmd; import org.apache.cloudstack.api.command.admin.resource.ListCapacityCmd; import org.apache.cloudstack.api.command.admin.resource.UploadCustomCertificateCmd; -import org.apache.cloudstack.api.command.admin.storage.ListStoragePoolsCmd; import org.apache.cloudstack.api.command.admin.systemvm.DestroySystemVmCmd; import org.apache.cloudstack.api.command.admin.systemvm.ListSystemVMsCmd; import org.apache.cloudstack.api.command.admin.systemvm.RebootSystemVmCmd; @@ -93,6 +92,7 @@ import com.cloud.agent.api.GetVncPortAnswer; import com.cloud.agent.api.GetVncPortCommand; import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeCommand; +import com.cloud.agent.manager.ClusteredAgentManagerImpl; import com.cloud.agent.manager.allocator.HostAllocator; import com.cloud.alert.Alert; import com.cloud.alert.AlertManager; @@ -186,7 +186,6 @@ import com.cloud.storage.GuestOsCategory; import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.StorageManager; -import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolVO; import com.cloud.storage.Upload; import com.cloud.storage.Upload.Mode; @@ -382,8 +381,14 @@ public class ManagementServerImpl implements ManagementServer { S3Manager _s3Mgr; @Inject - ComponentContext _placeholder; // create a dependency to ComponentContext so that it can be loaded beforehead - + ComponentContext _forceContextRef; // create a dependency to ComponentContext so that it can be loaded beforehead + + @Inject + EventUtils _forceEventUtilsRef; + + @Inject + CloudStackComponentComposer _componentRegistry; + private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker")); private KeystoreManager _ksMgr; @@ -394,7 +399,7 @@ public class ManagementServerImpl implements ManagementServer { @Inject List _userAuthenticators; private String _hashKey = null; - + public ManagementServerImpl() { } @@ -449,12 +454,11 @@ public class ManagementServerImpl implements ManagementServer { Map daos = ComponentContext.getApplicationContext().getBeansOfType( GenericDaoBase.class); + Map params = new HashMap(); for (GenericDaoBase dao : daos.values()) { try { s_logger.info("Starting dao " + ComponentContext.getTargetClass(dao).getName()); - - // TODO - // dao.configure(dao.getClass().getSimpleName(), params); + dao.configure(dao.getClass().getSimpleName(), params); } catch (Exception e) { s_logger.error("Problems with running checker:" + ComponentContext.getTargetClass(dao).getName(), e); System.exit(1); @@ -463,12 +467,8 @@ public class ManagementServerImpl implements ManagementServer { } private void startManagers() { - @SuppressWarnings("rawtypes") - Map managers = ComponentContext.getApplicationContext().getBeansOfType( - Manager.class); - - Map params = new HashMap(); - for(Manager manager : managers.values()) { + Map params = new HashMap(); + for(Manager manager : _componentRegistry.getManagers()) { s_logger.info("Start manager: " + ComponentContext.getTargetClass(manager).getName() + "..."); try { if(!manager.configure(manager.getClass().getSimpleName(), params)) { From f57dcaa820432d25b8ea541951f73d085110c148 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Fri, 11 Jan 2013 15:04:44 -0800 Subject: [PATCH 51/73] Loadable components to be in separted Spring component bundling --- api/src/com/cloud/user/UserContext.java | 1 - client/WEB-INF/web.xml | 2 +- client/tomcatconf/applicationContext.xml.in | 1 + client/tomcatconf/componentContext.xml.in | 52 +++++ .../network/element/NiciraNvpElement.java | 2 - .../com/cloud/network/element/OvsElement.java | 3 - server/src/com/cloud/api/ApiDBUtils.java | 189 +++++++++++++++++- server/src/com/cloud/api/ApiServer.java | 8 +- .../com/cloud/api/query/QueryManagerImpl.java | 3 +- .../com/cloud/event/dao/EventJoinDaoImpl.java | 4 +- .../com/cloud/network/NetworkManagerImpl.java | 5 +- .../network/element/BareMetalElement.java | 3 - .../element/CloudZonesNetworkElement.java | 3 - .../network/element/ExternalDhcpElement.java | 3 - .../network/element/SecurityGroupElement.java | 4 - .../network/element/VirtualRouterElement.java | 2 - .../element/VpcVirtualRouterElement.java | 2 - .../server/CloudStackComponentComposer.java | 185 +++++++++++++++++ .../cloud/server/ManagementServerExtImpl.java | 2 - .../cloud/server/ManagementServerImpl.java | 2 - .../com/cloud/storage/s3/S3ManagerImpl.java | 6 +- .../cloud/template/TemplateManagerImpl.java | 2 +- .../cloud/utils/component/AdapterBase.java | 4 + 23 files changed, 446 insertions(+), 42 deletions(-) create mode 100644 client/tomcatconf/componentContext.xml.in create mode 100644 server/src/com/cloud/server/CloudStackComponentComposer.java diff --git a/api/src/com/cloud/user/UserContext.java b/api/src/com/cloud/user/UserContext.java index 39da4eafd2e..54c01347097 100644 --- a/api/src/com/cloud/user/UserContext.java +++ b/api/src/com/cloud/user/UserContext.java @@ -16,7 +16,6 @@ // under the License. package com.cloud.user; - public class UserContext { private static ThreadLocal s_currentContext = new ThreadLocal(); diff --git a/client/WEB-INF/web.xml b/client/WEB-INF/web.xml index c6fd30fa3ac..0d75165659e 100644 --- a/client/WEB-INF/web.xml +++ b/client/WEB-INF/web.xml @@ -25,7 +25,7 @@ contextConfigLocation - classpath:applicationContext.xml + classpath:applicationContext.xml, classpath:componentContext.xml diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in index aabf9636d95..34bb853c52e 100644 --- a/client/tomcatconf/applicationContext.xml.in +++ b/client/tomcatconf/applicationContext.xml.in @@ -13,6 +13,7 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 36d4580ccb8..cf8d43a70bc 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 @@ -30,7 +30,6 @@ import javax.naming.ConfigurationException; import org.apache.cloudstack.network.ExternalNetworkDeviceManager.NetworkDevice; import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; import com.cloud.agent.api.ConfigurePortForwardingRulesOnLogicalRouterAnswer; @@ -121,7 +120,6 @@ import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.NicDao; -@Component @Local(value = NetworkElement.class) public class NiciraNvpElement extends AdapterBase implements ConnectivityProvider, SourceNatServiceProvider, diff --git a/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java b/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java index 6bc08f6c787..ebae66061b3 100644 --- a/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java +++ b/plugins/network-elements/ovs/src/com/cloud/network/element/OvsElement.java @@ -22,8 +22,6 @@ import java.util.Set; import javax.ejb.Local; import javax.inject.Inject; -import org.springframework.stereotype.Component; - import com.cloud.deploy.DeployDestination; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; @@ -42,7 +40,6 @@ import com.cloud.vm.ReservationContext; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; -@Component @Local(value = NetworkElement.class) public class OvsElement extends AdapterBase implements NetworkElement { @Inject diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java index 8a15dd12a7d..af061b11cff 100755 --- a/server/src/com/cloud/api/ApiDBUtils.java +++ b/server/src/com/cloud/api/ApiDBUtils.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import javax.annotation.PostConstruct; import javax.inject.Inject; import org.apache.cloudstack.api.ApiConstants.HostDetails; @@ -41,6 +42,7 @@ import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VolumeResponse; +import org.springframework.stereotype.Component; import com.cloud.api.query.dao.AccountJoinDao; import com.cloud.api.query.dao.AsyncJobJoinDao; @@ -249,6 +251,7 @@ import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; +@Component public class ApiDBUtils { private static ManagementServer _ms; @Inject static AsyncJobManager _asyncMgr; @@ -343,7 +346,191 @@ public class ApiDBUtils { @Inject static SnapshotPolicyDao _snapshotPolicyDao; @Inject static AsyncJobDao _asyncJobDao; - static { + @Inject private ManagementServer ms; + @Inject public AsyncJobManager asyncMgr; + @Inject private SecurityGroupManager securityGroupMgr; + @Inject private StorageManager storageMgr; + @Inject private UserVmManager userVmMgr; + @Inject private NetworkManager networkMgr; + @Inject private StatsCollector statsCollector; + + @Inject private AccountDao accountDao; + @Inject private AccountVlanMapDao accountVlanMapDao; + @Inject private ClusterDao clusterDao; + @Inject private CapacityDao capacityDao; + @Inject private DiskOfferingDao diskOfferingDao; + @Inject private DomainDao domainDao; + @Inject private DomainRouterDao domainRouterDao; + @Inject private DomainRouterJoinDao domainRouterJoinDao; + @Inject private GuestOSDao guestOSDao; + @Inject private GuestOSCategoryDao guestOSCategoryDao; + @Inject private HostDao hostDao; + @Inject private IPAddressDao ipAddressDao; + @Inject private LoadBalancerDao loadBalancerDao; + @Inject private SecurityGroupDao securityGroupDao; + @Inject private SecurityGroupJoinDao securityGroupJoinDao; + @Inject private NetworkRuleConfigDao networkRuleConfigDao; + @Inject private HostPodDao podDao; + @Inject private ServiceOfferingDao serviceOfferingDao; + @Inject private SnapshotDao snapshotDao; + @Inject private StoragePoolDao storagePoolDao; + @Inject private VMTemplateDao templateDao; + @Inject private VMTemplateDetailsDao templateDetailsDao; + @Inject private VMTemplateHostDao templateHostDao; + @Inject private VMTemplateSwiftDao templateSwiftDao; + @Inject private VMTemplateS3Dao templateS3Dao; + @Inject private UploadDao uploadDao; + @Inject private UserDao userDao; + @Inject private UserStatisticsDao userStatsDao; + @Inject private UserVmDao userVmDao; + @Inject private UserVmJoinDao userVmJoinDao; + @Inject private VlanDao vlanDao; + @Inject private VolumeDao volumeDao; + @Inject private Site2SiteVpnGatewayDao site2SiteVpnGatewayDao; + @Inject private Site2SiteCustomerGatewayDao site2SiteCustomerGatewayDao; + @Inject private VolumeHostDao volumeHostDao; + @Inject private DataCenterDao zoneDao; + @Inject private NetworkOfferingDao networkOfferingDao; + @Inject private NetworkDao networkDao; + @Inject private PhysicalNetworkDao physicalNetworkDao; + @Inject private ConfigurationService configMgr; + @Inject private ConfigurationDao configDao; + @Inject private ConsoleProxyDao consoleProxyDao; + @Inject private FirewallRulesCidrsDao firewallCidrsDao; + @Inject private VMInstanceDao vmDao; + @Inject private ResourceLimitService resourceLimitMgr; + @Inject private ProjectService projectMgr; + @Inject private ResourceManager resourceMgr; + @Inject private AccountDetailsDao accountDetailsDao; + @Inject private NetworkDomainDao networkDomainDao; + @Inject private HighAvailabilityManager haMgr; + @Inject private VpcManager vpcMgr; + @Inject private TaggedResourceService taggedResourceService; + @Inject private UserVmDetailsDao userVmDetailsDao; + @Inject private SSHKeyPairDao sshKeyPairDao; + + @Inject private ConditionDao asConditionDao; + @Inject private AutoScalePolicyConditionMapDao asPolicyConditionMapDao; + @Inject private AutoScaleVmGroupPolicyMapDao asVmGroupPolicyMapDao; + @Inject private AutoScalePolicyDao asPolicyDao; + @Inject private AutoScaleVmProfileDao asVmProfileDao; + @Inject private AutoScaleVmGroupDao asVmGroupDao; + @Inject private CounterDao counterDao; + @Inject private ResourceTagJoinDao tagJoinDao; + @Inject private EventJoinDao eventJoinDao; + @Inject private InstanceGroupJoinDao vmGroupJoinDao; + @Inject private UserAccountJoinDao userAccountJoinDao; + @Inject private ProjectJoinDao projectJoinDao; + @Inject private ProjectAccountJoinDao projectAccountJoinDao; + @Inject private ProjectInvitationJoinDao projectInvitationJoinDao; + @Inject private HostJoinDao hostJoinDao; + @Inject private VolumeJoinDao volJoinDao; + @Inject private StoragePoolJoinDao poolJoinDao; + @Inject private AccountJoinDao accountJoinDao; + @Inject private AsyncJobJoinDao jobJoinDao; + + @Inject private PhysicalNetworkTrafficTypeDao physicalNetworkTrafficTypeDao; + @Inject private PhysicalNetworkServiceProviderDao physicalNetworkServiceProviderDao; + @Inject private FirewallRulesDao firewallRuleDao; + @Inject private StaticRouteDao staticRouteDao; + @Inject private VpcGatewayDao vpcGatewayDao; + @Inject private VpcDao vpcDao; + @Inject private VpcOfferingDao vpcOfferingDao; + @Inject private SnapshotPolicyDao snapshotPolicyDao; + @Inject private AsyncJobDao asyncJobDao; + + @PostConstruct + void init() { + _ms = ms; + _asyncMgr = asyncMgr; + _securityGroupMgr = securityGroupMgr; + _storageMgr = storageMgr; + _userVmMgr = userVmMgr; + _networkMgr = networkMgr; + _configMgr = configMgr; + + _accountDao = accountDao; + _accountVlanMapDao = accountVlanMapDao; + _clusterDao = clusterDao; + _capacityDao = capacityDao; + _diskOfferingDao = diskOfferingDao; + _domainDao = domainDao; + _domainRouterDao = domainRouterDao; + _domainRouterJoinDao = domainRouterJoinDao; + _guestOSDao = guestOSDao; + _guestOSCategoryDao = guestOSCategoryDao; + _hostDao = hostDao; + _ipAddressDao = ipAddressDao; + _loadBalancerDao = loadBalancerDao; + _networkRuleConfigDao = networkRuleConfigDao; + _podDao = podDao; + _serviceOfferingDao = serviceOfferingDao; + _snapshotDao = snapshotDao; + _storagePoolDao = storagePoolDao; + _templateDao = templateDao; + _templateDetailsDao = templateDetailsDao; + _templateHostDao = templateHostDao; + _templateSwiftDao = templateSwiftDao; + _templateS3Dao = templateS3Dao; + _uploadDao = uploadDao; + _userDao = userDao; + _userStatsDao = userStatsDao; + _userVmDao = userVmDao; + _userVmJoinDao = userVmJoinDao; + _vlanDao = vlanDao; + _volumeDao = volumeDao; + _site2SiteVpnGatewayDao = site2SiteVpnGatewayDao; + _site2SiteCustomerGatewayDao = site2SiteCustomerGatewayDao; + _volumeHostDao = volumeHostDao; + _zoneDao = zoneDao; + _securityGroupDao = securityGroupDao; + _securityGroupJoinDao = securityGroupJoinDao; + _networkOfferingDao = networkOfferingDao; + _networkDao = networkDao; + _physicalNetworkDao = physicalNetworkDao; + _configDao = configDao; + _consoleProxyDao = consoleProxyDao; + _firewallCidrsDao = firewallCidrsDao; + _vmDao = vmDao; + _resourceLimitMgr = resourceLimitMgr; + _projectMgr = projectMgr; + _resourceMgr = resourceMgr; + _accountDetailsDao = accountDetailsDao; + _networkDomainDao = networkDomainDao; + _haMgr = haMgr; + _vpcMgr = vpcMgr; + _taggedResourceService = taggedResourceService; + _sshKeyPairDao = sshKeyPairDao; + _userVmDetailsDao = userVmDetailsDao; + _asConditionDao = asConditionDao; + _asPolicyDao = asPolicyDao; + _asPolicyConditionMapDao = asPolicyConditionMapDao; + _counterDao = counterDao; + _asVmGroupPolicyMapDao = asVmGroupPolicyMapDao; + _tagJoinDao = tagJoinDao; + _vmGroupJoinDao = vmGroupJoinDao; + _eventJoinDao = eventJoinDao; + _userAccountJoinDao = userAccountJoinDao; + _projectJoinDao = projectJoinDao; + _projectAccountJoinDao = projectAccountJoinDao; + _projectInvitationJoinDao = projectInvitationJoinDao; + _hostJoinDao = hostJoinDao; + _volJoinDao = volJoinDao; + _poolJoinDao = poolJoinDao; + _accountJoinDao = accountJoinDao; + _jobJoinDao = jobJoinDao; + + _physicalNetworkTrafficTypeDao = physicalNetworkTrafficTypeDao; + _physicalNetworkServiceProviderDao = physicalNetworkServiceProviderDao; + _firewallRuleDao = firewallRuleDao; + _staticRouteDao = staticRouteDao; + _vpcGatewayDao = vpcGatewayDao; + _asVmProfileDao = asVmProfileDao; + _asVmGroupDao = asVmGroupDao; + _vpcDao = vpcDao; + _vpcOfferingDao = vpcOfferingDao; + _snapshotPolicyDao = snapshotPolicyDao; + _asyncJobDao = asyncJobDao; // Note: stats collector should already have been initialized by this time, otherwise a null instance is returned _statsCollector = StatsCollector.getInstance(); diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index b4c0e2760f8..4cb892f448a 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -148,8 +148,8 @@ public class ApiServer implements HttpRequestHandler { @Inject ApiDispatcher _dispatcher; @Inject private AccountManager _accountMgr; - @Inject private DomainManager _domainMgr = null; - @Inject private AsyncJobManager _asyncMgr = null; + @Inject private DomainManager _domainMgr; + @Inject private AsyncJobManager _asyncMgr; @Inject private ConfigurationDao _configDao; @Inject List _pluggableServices; @@ -552,7 +552,7 @@ public class ApiServer implements HttpRequestHandler { } String commandName = command[0]; - +/* // if userId not null, that mean that user is logged in if (userId != null) { User user = ApiDBUtils.findUserById(userId); @@ -568,7 +568,7 @@ public class ApiServer implements HttpRequestHandler { throw new ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command does not exist or it is not available for user"); } } - +*/ // - build a request string with sorted params, make sure it's all lowercase // - sign the request, verify the signature is the same List parameterNames = new ArrayList(); diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index 116e7113066..6760dec2f44 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -214,8 +214,7 @@ public class QueryManagerImpl implements QueryService, Manager { @Override public boolean configure(String name, Map params) throws ConfigurationException { _name = name; - // _responseGenerator = new ViewResponseHelper(); - return false; + return true; } @Override diff --git a/server/src/com/cloud/event/dao/EventJoinDaoImpl.java b/server/src/com/cloud/event/dao/EventJoinDaoImpl.java index 764df99557f..873ee292224 100644 --- a/server/src/com/cloud/event/dao/EventJoinDaoImpl.java +++ b/server/src/com/cloud/event/dao/EventJoinDaoImpl.java @@ -27,6 +27,8 @@ import com.cloud.api.ApiResponseHelper; import com.cloud.api.query.vo.EventJoinVO; import org.apache.cloudstack.api.response.EventResponse; +import org.springframework.stereotype.Component; + import com.cloud.event.Event; import com.cloud.event.Event.State; import com.cloud.utils.db.Filter; @@ -34,7 +36,7 @@ import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; - +@Component @Local(value={EventJoinDao.class}) public class EventJoinDaoImpl extends GenericDaoBase implements EventJoinDao { public static final Logger s_logger = Logger.getLogger(EventJoinDaoImpl.class); diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index bca676e00a2..f74abfc0807 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -133,6 +133,7 @@ import com.cloud.utils.AnnotationHelper; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.JoinBuilder.JoinType; @@ -212,11 +213,9 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag @Inject PodVlanMapDao _podVlanMapDao; - //@com.cloud.utils.component.Inject(adapter = NetworkGuru.class) @Inject List _networkGurus; - // @com.cloud.utils.component.Inject(adapter = NetworkElement.class) @Inject List _networkElements; @@ -1540,6 +1539,8 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag "multiple NetworkElements found for Provider: " + implementedProvider.getName()); return false; } + s_logger.info("add element/provider mapping. provider: " + implementedProvider.getName() + " -> " + element.getName() + + ", class: " + ComponentContext.getTargetClass(element).getName()); s_providerToNetworkElementMap.put(implementedProvider.getName(), element.getName()); } if (capabilities != null && implementedProvider != null) { diff --git a/server/src/com/cloud/network/element/BareMetalElement.java b/server/src/com/cloud/network/element/BareMetalElement.java index d13cf141c0d..553fe1d63b2 100644 --- a/server/src/com/cloud/network/element/BareMetalElement.java +++ b/server/src/com/cloud/network/element/BareMetalElement.java @@ -16,7 +16,6 @@ // under the License. package com.cloud.network.element; -import java.util.List; import java.util.Map; import java.util.Set; @@ -24,7 +23,6 @@ import javax.ejb.Local; import javax.inject.Inject; import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; import com.cloud.baremetal.ExternalDhcpManager; import com.cloud.deploy.DeployDestination; @@ -49,7 +47,6 @@ import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.NicDao; -@Component @Local(value=NetworkElement.class) public class BareMetalElement extends AdapterBase implements NetworkElement { private static final Logger s_logger = Logger.getLogger(BareMetalElement.class); diff --git a/server/src/com/cloud/network/element/CloudZonesNetworkElement.java b/server/src/com/cloud/network/element/CloudZonesNetworkElement.java index 0cf632cb666..cc3546084de 100644 --- a/server/src/com/cloud/network/element/CloudZonesNetworkElement.java +++ b/server/src/com/cloud/network/element/CloudZonesNetworkElement.java @@ -17,7 +17,6 @@ package com.cloud.network.element; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; @@ -25,7 +24,6 @@ import javax.ejb.Local; import javax.inject.Inject; import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; import com.cloud.agent.AgentManager.OnError; @@ -64,7 +62,6 @@ import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.UserVmDao; -@Component @Local(value = NetworkElement.class) public class CloudZonesNetworkElement extends AdapterBase implements NetworkElement, UserDataServiceProvider { private static final Logger s_logger = Logger.getLogger(CloudZonesNetworkElement.class); diff --git a/server/src/com/cloud/network/element/ExternalDhcpElement.java b/server/src/com/cloud/network/element/ExternalDhcpElement.java index 0f99abdc698..f7c465ddd35 100755 --- a/server/src/com/cloud/network/element/ExternalDhcpElement.java +++ b/server/src/com/cloud/network/element/ExternalDhcpElement.java @@ -17,7 +17,6 @@ package com.cloud.network.element; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; @@ -25,7 +24,6 @@ import javax.ejb.Local; import javax.inject.Inject; import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; import com.cloud.baremetal.ExternalDhcpManager; import com.cloud.dc.DataCenter; @@ -51,7 +49,6 @@ import com.cloud.vm.ReservationContext; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; -@Component @Local(value = NetworkElement.class) public class ExternalDhcpElement extends AdapterBase implements NetworkElement, DhcpServiceProvider { private static final Logger s_logger = Logger.getLogger(ExternalDhcpElement.class); diff --git a/server/src/com/cloud/network/element/SecurityGroupElement.java b/server/src/com/cloud/network/element/SecurityGroupElement.java index bcc8ecc9269..0659db781e3 100644 --- a/server/src/com/cloud/network/element/SecurityGroupElement.java +++ b/server/src/com/cloud/network/element/SecurityGroupElement.java @@ -17,14 +17,11 @@ package com.cloud.network.element; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; import javax.ejb.Local; -import org.springframework.stereotype.Component; - import com.cloud.deploy.DeployDestination; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; @@ -42,7 +39,6 @@ import com.cloud.vm.ReservationContext; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; -@Component @Local(value=NetworkElement.class) public class SecurityGroupElement extends AdapterBase implements NetworkElement { private static final Map> capabilities = setCapabilities(); diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index b25d4c47876..8a69e56bd2c 100755 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VirtualRouterElement.java @@ -29,7 +29,6 @@ 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; -import org.springframework.stereotype.Component; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.dao.ConfigurationDao; @@ -90,7 +89,6 @@ import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.UserVmDao; import com.google.gson.Gson; -@Component @Local(value = NetworkElement.class) public class VirtualRouterElement extends AdapterBase implements VirtualRouterElementService, DhcpServiceProvider, UserDataServiceProvider, SourceNatServiceProvider, StaticNatServiceProvider, FirewallServiceProvider, diff --git a/server/src/com/cloud/network/element/VpcVirtualRouterElement.java b/server/src/com/cloud/network/element/VpcVirtualRouterElement.java index 49ca0b4878d..60924a2a6ab 100644 --- a/server/src/com/cloud/network/element/VpcVirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VpcVirtualRouterElement.java @@ -26,7 +26,6 @@ import javax.ejb.Local; import javax.inject.Inject; import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; import com.cloud.dc.DataCenter; import com.cloud.deploy.DeployDestination; @@ -63,7 +62,6 @@ import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineProfile; -@Component @Local(value = NetworkElement.class) public class VpcVirtualRouterElement extends VirtualRouterElement implements VpcProvider, Site2SiteVpnServiceProvider, NetworkACLServiceProvider{ private static final Logger s_logger = Logger.getLogger(VpcVirtualRouterElement.class); diff --git a/server/src/com/cloud/server/CloudStackComponentComposer.java b/server/src/com/cloud/server/CloudStackComponentComposer.java new file mode 100644 index 00000000000..6bd9bc5db45 --- /dev/null +++ b/server/src/com/cloud/server/CloudStackComponentComposer.java @@ -0,0 +1,185 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.server; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +import org.springframework.stereotype.Component; + +import com.cloud.agent.AgentManager; +import com.cloud.alert.AlertManagerImpl; +import com.cloud.api.query.QueryManagerImpl; +import com.cloud.async.AsyncJobManager; +import com.cloud.async.SyncQueueManager; +import com.cloud.capacity.CapacityManagerImpl; +import com.cloud.cluster.CheckPointManagerImpl; +import com.cloud.cluster.ClusterFenceManagerImpl; +import com.cloud.cluster.ClusterManagerImpl; +import com.cloud.configuration.ConfigurationManager; +import com.cloud.consoleproxy.ConsoleProxyManager; +import com.cloud.dao.EntityManagerImpl; +import com.cloud.ha.HighAvailabilityManager; +import com.cloud.hypervisor.HypervisorGuruManagerImpl; +import com.cloud.keystore.KeystoreManager; +import com.cloud.maint.UpgradeManagerImpl; +import com.cloud.network.ExternalLoadBalancerUsageManager; +import com.cloud.network.NetworkManagerImpl; +import com.cloud.network.StorageNetworkManager; +import com.cloud.network.as.AutoScaleManagerImpl; +import com.cloud.network.firewall.FirewallManagerImpl; +import com.cloud.network.lb.LoadBalancingRulesManagerImpl; +import com.cloud.network.router.VpcVirtualNetworkApplianceManager; +import com.cloud.network.rules.RulesManagerImpl; +import com.cloud.network.security.SecurityGroupManagerImpl2; +import com.cloud.network.vpc.NetworkACLManagerImpl; +import com.cloud.network.vpc.VpcManagerImpl; +import com.cloud.network.vpn.RemoteAccessVpnManagerImpl; +import com.cloud.network.vpn.Site2SiteVpnManagerImpl; +import com.cloud.projects.ProjectManagerImpl; +import com.cloud.resource.ResourceManagerImpl; +import com.cloud.resourcelimit.ResourceLimitManagerImpl; +import com.cloud.storage.OCFS2Manager; +import com.cloud.storage.StorageManagerImpl; +import com.cloud.storage.download.DownloadMonitor; +import com.cloud.storage.s3.S3Manager; +import com.cloud.storage.secondary.SecondaryStorageManagerImpl; +import com.cloud.storage.snapshot.SnapshotManagerImpl; +import com.cloud.storage.snapshot.SnapshotSchedulerImpl; +import com.cloud.storage.swift.SwiftManager; +import com.cloud.storage.upload.UploadMonitor; +import com.cloud.tags.TaggedResourceManagerImpl; +import com.cloud.template.TemplateManagerImpl; +import com.cloud.user.AccountManagerImpl; +import com.cloud.user.DomainManagerImpl; +import com.cloud.utils.component.Manager; +import com.cloud.vm.UserVmManagerImpl; +import com.cloud.vm.VirtualMachineManager; + +@Component +public class CloudStackComponentComposer { + @Inject CheckPointManagerImpl _checkPointMgr; + @Inject ClusterManagerImpl _clusterMgr; + @Inject ClusterFenceManagerImpl _clusterFenceMgr; + @Inject AgentManager _AgentMgr; + @Inject SyncQueueManager _sycnQueueMgr; + @Inject AsyncJobManager _jobMgr; + @Inject ConfigurationManager _confMgr; + @Inject AccountManagerImpl _accountMgr; + @Inject DomainManagerImpl _domainMgr; + @Inject ResourceLimitManagerImpl _resLimitMgr; + @Inject NetworkManagerImpl _networkMgr; + @Inject DownloadMonitor _downloadMonitor; + @Inject UploadMonitor _uploadMonitor; + @Inject KeystoreManager _ksMgr; + @Inject SecondaryStorageManagerImpl _ssMgr; + @Inject UserVmManagerImpl _userVmMgr; + @Inject UpgradeManagerImpl _upgradeMgr; + @Inject StorageManagerImpl _storageMgr; + @Inject AlertManagerImpl _alertMgr; + @Inject TemplateManagerImpl _tmplMgr; + @Inject SnapshotManagerImpl _snpahsotMgr; + @Inject SnapshotSchedulerImpl _snapshotScheduleMgr; + @Inject SecurityGroupManagerImpl2 _sgMgr; + @Inject EntityManagerImpl _entityMgr; + @Inject LoadBalancingRulesManagerImpl _lbRuleMgr; + @Inject AutoScaleManagerImpl _asMgr; + @Inject RulesManagerImpl _rulesMgr; + @Inject RemoteAccessVpnManagerImpl _acVpnMgr; + @Inject CapacityManagerImpl _capacityMgr; + @Inject VirtualMachineManager _vmMgr; + @Inject HypervisorGuruManagerImpl _hvGuruMgr; + @Inject ResourceManagerImpl _resMgr; + @Inject OCFS2Manager _ocfsMgr; + @Inject FirewallManagerImpl _fwMgr; + @Inject ConsoleProxyManager _cpMgr; + @Inject ProjectManagerImpl _prjMgr; + @Inject SwiftManager _swiftMgr; + @Inject S3Manager _s3Mgr; + @Inject StorageNetworkManager _storageNetworkMgr; + @Inject ExternalLoadBalancerUsageManager _extlbUsageMgr; + @Inject HighAvailabilityManager _haMgr; + @Inject VpcManagerImpl _vpcMgr; + @Inject VpcVirtualNetworkApplianceManager _vpcNetApplianceMgr; + @Inject NetworkACLManagerImpl _networkAclMgr; + @Inject TaggedResourceManagerImpl _taggedResMgr; + @Inject Site2SiteVpnManagerImpl _s2sVpnMgr; + @Inject QueryManagerImpl _queryMgr; + + List _managers = new ArrayList(); + + public CloudStackComponentComposer() { + } + + @PostConstruct + void init() { + _managers.add(_checkPointMgr); + _managers.add(_clusterMgr); + _managers.add(_clusterFenceMgr); + _managers.add(_AgentMgr); + _managers.add(_sycnQueueMgr); + _managers.add(_jobMgr); + _managers.add(_confMgr); + _managers.add(_accountMgr); + _managers.add(_domainMgr); + _managers.add(_resLimitMgr); + _managers.add(_networkMgr); + _managers.add(_downloadMonitor); + _managers.add(_uploadMonitor); + _managers.add(_ksMgr); + _managers.add(_ssMgr); + _managers.add(_userVmMgr); + _managers.add(_upgradeMgr); + _managers.add(_storageMgr); + _managers.add(_alertMgr); + _managers.add(_tmplMgr); + _managers.add(_snpahsotMgr); + _managers.add(_snapshotScheduleMgr); + _managers.add(_sgMgr); + _managers.add(_entityMgr); + _managers.add(_lbRuleMgr); + _managers.add(_asMgr); + _managers.add(_rulesMgr); + _managers.add(_acVpnMgr); + _managers.add(_capacityMgr); + _managers.add(_vmMgr); + _managers.add(_hvGuruMgr); + _managers.add(_resMgr); + _managers.add(_ocfsMgr); + _managers.add(_fwMgr); + _managers.add(_cpMgr); + _managers.add(_prjMgr); + _managers.add(_swiftMgr); + _managers.add(_s3Mgr); + _managers.add(_storageNetworkMgr); + _managers.add(_extlbUsageMgr); + _managers.add(_haMgr); + _managers.add(_vpcMgr); + _managers.add(_vpcNetApplianceMgr); + _managers.add(_networkAclMgr); + _managers.add(_taggedResMgr); + _managers.add(_s2sVpnMgr); + _managers.add(_queryMgr); + } + + public List getManagers() { + return _managers; + } +} diff --git a/server/src/com/cloud/server/ManagementServerExtImpl.java b/server/src/com/cloud/server/ManagementServerExtImpl.java index 310698536b0..ed05395d45c 100644 --- a/server/src/com/cloud/server/ManagementServerExtImpl.java +++ b/server/src/com/cloud/server/ManagementServerExtImpl.java @@ -34,7 +34,6 @@ import com.cloud.exception.PermissionDeniedException; import com.cloud.projects.Project; import com.cloud.utils.PropertiesUtil; import org.apache.cloudstack.api.response.UsageTypeResponse; -import org.springframework.stereotype.Component; import com.cloud.usage.UsageJobVO; import com.cloud.usage.UsageTypes; @@ -49,7 +48,6 @@ import com.cloud.utils.db.Filter; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; -@Component public class ManagementServerExtImpl extends ManagementServerImpl implements ManagementServerExt { @Inject private AccountDao _accountDao; @Inject private DomainDao _domainDao; diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index c9a59040216..65501e3b416 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -85,14 +85,12 @@ import org.apache.cloudstack.api.command.user.zone.ListZonesByCmd; import org.apache.cloudstack.api.response.ExtractResponse; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; import com.cloud.agent.api.GetVncPortAnswer; import com.cloud.agent.api.GetVncPortCommand; import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeCommand; -import com.cloud.agent.manager.ClusteredAgentManagerImpl; import com.cloud.agent.manager.allocator.HostAllocator; import com.cloud.alert.Alert; import com.cloud.alert.AlertManager; diff --git a/server/src/com/cloud/storage/s3/S3ManagerImpl.java b/server/src/com/cloud/storage/s3/S3ManagerImpl.java index 62db0bb671d..16e2ad900db 100644 --- a/server/src/com/cloud/storage/s3/S3ManagerImpl.java +++ b/server/src/com/cloud/storage/s3/S3ManagerImpl.java @@ -41,6 +41,7 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.Callable; +import javax.annotation.PostConstruct; import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; @@ -119,10 +120,9 @@ public class S3ManagerImpl implements S3Manager { @Inject private SecondaryStorageVmManager secondaryStorageVMManager; - protected S3ManagerImpl() { - super(); + public S3ManagerImpl() { } - + private void verifyConnection(final S3TO s3) throws DiscoveryException { if (!canConnect(s3)) { diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index 3a8bec5ff87..1ce9578408f 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -1098,7 +1098,7 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe s_logger.info("S3 secondary storage synchronization is disabled."); } - return false; + return true; } protected TemplateManagerImpl() { diff --git a/utils/src/com/cloud/utils/component/AdapterBase.java b/utils/src/com/cloud/utils/component/AdapterBase.java index 7b75993776b..8fd374aebee 100644 --- a/utils/src/com/cloud/utils/component/AdapterBase.java +++ b/utils/src/com/cloud/utils/component/AdapterBase.java @@ -35,6 +35,10 @@ public class AdapterBase implements Adapter { public String getName() { return _name; } + + public void setName(String name) { + _name = name; + } @Override public boolean start() { From 6fb1a1e6f113a3714c5abb314be91cc56195145e Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Fri, 11 Jan 2013 16:54:32 -0800 Subject: [PATCH 52/73] Fix issues after another round of merge --- .../CloudZonesComponentLibrary.java | 37 -- .../DefaultComponentLibrary.java | 495 ------------------ .../PremiumComponentLibrary.java | 72 --- .../server/CloudStackComponentComposer.java | 5 +- .../cloud/servlet/ConsoleProxyServlet.java | 14 +- .../servlet/RegisterCompleteServlet.java | 3 +- server/src/com/cloud/test/DatabaseConfig.java | 2 +- .../com/cloud/async/TestAsyncJobManager.java | 5 +- .../com/cloud/async/TestSyncQueueManager.java | 8 +- .../SecurityGroupManagerImpl2Test.java | 12 +- .../com/cloud/snapshot/SnapshotDaoTest.java | 7 +- .../cloud/storage/dao/StoragePoolDaoTest.java | 4 +- .../AdvanceZone217To224UpgradeTest.java | 9 +- .../AdvanceZone223To224UpgradeTest.java | 6 +- .../upgrade/BasicZone218To224UpgradeTest.java | 8 +- .../upgrade/HostCapacity218to22Test.java | 8 +- .../InstanceGroup218To224UpgradeTest.java | 8 +- .../PortForwarding218To224UpgradeTest.java | 8 +- .../upgrade/Sanity220To224UpgradeTest.java | 8 +- .../upgrade/Sanity222To224UpgradeTest.java | 7 +- .../upgrade/Sanity223To225UpgradeTest.java | 8 +- .../upgrade/Sanity224To225UpgradeTest.java | 8 +- .../upgrade/Template2214To30UpgradeTest.java | 5 +- .../cloud/upgrade/Test2214To30DBUpgrade.java | 8 +- .../upgrade/Usage217To224UpgradeTest.java | 7 +- .../UsageEvents218To224UpgradeTest.java | 8 +- .../com/cloud/vm/dao/UserVmDaoImplTest.java | 7 +- .../com/cloud/vpc/MockNetworkManagerImpl.java | 1 - .../com/cloud/vpc/MockVpcManagerImpl.java | 3 +- server/test/com/cloud/vpc/VpcApiUnitTest.java | 39 +- .../src/com/cloud/usage/UsageManagerImpl.java | 40 +- usage/src/com/cloud/usage/UsageServer.java | 4 +- .../usage/parser/IPAddressUsageParser.java | 20 +- .../usage/parser/LoadBalancerUsageParser.java | 20 +- .../parser/NetworkOfferingUsageParser.java | 17 +- .../usage/parser/NetworkUsageParser.java | 19 +- .../parser/PortForwardingUsageParser.java | 17 +- .../parser/SecurityGroupUsageParser.java | 17 +- .../usage/parser/StorageUsageParser.java | 17 +- .../usage/parser/VMInstanceUsageParser.java | 17 +- .../usage/parser/VPNUserUsageParser.java | 17 +- .../cloud/usage/parser/VolumeUsageParser.java | 17 +- 42 files changed, 277 insertions(+), 765 deletions(-) delete mode 100644 server/src/com/cloud/configuration/CloudZonesComponentLibrary.java delete mode 100755 server/src/com/cloud/configuration/DefaultComponentLibrary.java delete mode 100755 server/src/com/cloud/configuration/PremiumComponentLibrary.java diff --git a/server/src/com/cloud/configuration/CloudZonesComponentLibrary.java b/server/src/com/cloud/configuration/CloudZonesComponentLibrary.java deleted file mode 100644 index 13bb16f26b7..00000000000 --- a/server/src/com/cloud/configuration/CloudZonesComponentLibrary.java +++ /dev/null @@ -1,37 +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. -package com.cloud.configuration; - -import com.cloud.agent.StartupCommandProcessor; -import com.cloud.agent.manager.authn.impl.BasicAgentAuthManager; - -import com.cloud.hypervisor.CloudZonesStartupProcessor; -import com.cloud.network.element.CloudZonesNetworkElement; -import com.cloud.network.element.NetworkElement; - - - -public class CloudZonesComponentLibrary extends PremiumComponentLibrary { - - @Override - protected void populateAdapters() { - super.populateAdapters(); - addAdapter(NetworkElement.class, "CloudZones", CloudZonesNetworkElement.class); - addAdapter(StartupCommandProcessor.class, "BasicAgentAuthorizer", BasicAgentAuthManager.class); - addAdapter(StartupCommandProcessor.class, "CloudZonesStartupProcessor", CloudZonesStartupProcessor.class); - } -} diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java deleted file mode 100755 index 2bb54094a82..00000000000 --- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java +++ /dev/null @@ -1,495 +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. -package com.cloud.configuration; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.cloud.agent.manager.ClusteredAgentManagerImpl; -import com.cloud.alert.AlertManagerImpl; -import com.cloud.alert.dao.AlertDaoImpl; -import com.cloud.api.query.QueryManagerImpl; -import com.cloud.api.query.dao.AccountJoinDaoImpl; -import com.cloud.api.query.dao.AsyncJobJoinDaoImpl; -import com.cloud.api.query.dao.DomainRouterJoinDaoImpl; -import com.cloud.api.query.dao.InstanceGroupJoinDaoImpl; -import com.cloud.api.query.dao.ProjectAccountJoinDaoImpl; -import com.cloud.api.query.dao.ProjectInvitationJoinDaoImpl; -import com.cloud.api.query.dao.ProjectJoinDaoImpl; -import com.cloud.api.query.dao.ResourceTagJoinDaoImpl; -import com.cloud.api.query.dao.SecurityGroupJoinDaoImpl; -import com.cloud.api.query.dao.StoragePoolJoinDaoImpl; -import com.cloud.api.query.dao.UserAccountJoinDaoImpl; -import com.cloud.api.query.dao.UserVmJoinDaoImpl; -import com.cloud.api.query.dao.HostJoinDaoImpl; -import com.cloud.api.query.dao.VolumeJoinDaoImpl; -import com.cloud.async.AsyncJobExecutorContextImpl; -import com.cloud.async.AsyncJobManagerImpl; -import com.cloud.async.SyncQueueManagerImpl; -import com.cloud.async.dao.AsyncJobDaoImpl; -import com.cloud.async.dao.SyncQueueDaoImpl; -import com.cloud.async.dao.SyncQueueItemDaoImpl; -import com.cloud.capacity.CapacityManagerImpl; -import com.cloud.capacity.dao.CapacityDaoImpl; -import com.cloud.certificate.dao.CertificateDaoImpl; -import com.cloud.cluster.CheckPointManagerImpl; -import com.cloud.cluster.ClusterFenceManagerImpl; -import com.cloud.cluster.ClusterManagerImpl; -import com.cloud.cluster.agentlb.dao.HostTransferMapDaoImpl; -import com.cloud.cluster.dao.ManagementServerHostDaoImpl; -import com.cloud.cluster.dao.ManagementServerHostPeerDaoImpl; -import com.cloud.cluster.dao.StackMaidDaoImpl; -import com.cloud.configuration.dao.ConfigurationDaoImpl; -import com.cloud.configuration.dao.ResourceCountDaoImpl; -import com.cloud.configuration.dao.ResourceLimitDaoImpl; -import com.cloud.consoleproxy.ConsoleProxyManagerImpl; -import com.cloud.dao.EntityManager; -import com.cloud.dao.EntityManagerImpl; -import com.cloud.dc.ClusterDetailsDaoImpl; -import com.cloud.dc.dao.AccountVlanMapDaoImpl; -import com.cloud.dc.dao.ClusterDaoImpl; -import com.cloud.dc.dao.ClusterVSMMapDaoImpl; -import com.cloud.dc.dao.DataCenterDaoImpl; -import com.cloud.dc.dao.DataCenterIpAddressDaoImpl; -import com.cloud.dc.dao.DcDetailsDaoImpl; -import com.cloud.dc.dao.HostPodDaoImpl; -import com.cloud.dc.dao.PodVlanMapDaoImpl; -import com.cloud.dc.dao.StorageNetworkIpAddressDaoImpl; -import com.cloud.dc.dao.StorageNetworkIpRangeDaoImpl; -import com.cloud.dc.dao.VlanDaoImpl; -import com.cloud.domain.dao.DomainDaoImpl; -import com.cloud.event.dao.EventDaoImpl; -import com.cloud.event.dao.UsageEventDaoImpl; -import com.cloud.ha.HighAvailabilityManagerImpl; -import com.cloud.ha.dao.HighAvailabilityDaoImpl; -import com.cloud.host.dao.HostDaoImpl; -import com.cloud.host.dao.HostDetailsDaoImpl; -import com.cloud.host.dao.HostTagsDaoImpl; -import com.cloud.hypervisor.HypervisorGuruManagerImpl; -import com.cloud.hypervisor.dao.HypervisorCapabilitiesDaoImpl; -import com.cloud.keystore.KeystoreDaoImpl; -import com.cloud.keystore.KeystoreManagerImpl; -import com.cloud.maint.UpgradeManagerImpl; -import com.cloud.maint.dao.AgentUpgradeDaoImpl; -import com.cloud.network.ExternalLoadBalancerUsageManagerImpl; -import com.cloud.network.NetworkManagerImpl; -import com.cloud.network.StorageNetworkManagerImpl; -import com.cloud.network.as.AutoScaleManagerImpl; -import com.cloud.network.as.dao.AutoScalePolicyConditionMapDaoImpl; -import com.cloud.network.as.dao.AutoScalePolicyDaoImpl; -import com.cloud.network.as.dao.AutoScaleVmGroupDaoImpl; -import com.cloud.network.as.dao.AutoScaleVmGroupPolicyMapDaoImpl; -import com.cloud.network.as.dao.AutoScaleVmProfileDaoImpl; -import com.cloud.network.as.dao.ConditionDaoImpl; -import com.cloud.network.as.dao.CounterDaoImpl; -import com.cloud.network.dao.ExternalFirewallDeviceDaoImpl; -import com.cloud.network.dao.ExternalLoadBalancerDeviceDaoImpl; -import com.cloud.network.dao.FirewallRulesCidrsDaoImpl; -import com.cloud.network.dao.FirewallRulesDaoImpl; -import com.cloud.network.dao.IPAddressDaoImpl; -import com.cloud.network.dao.InlineLoadBalancerNicMapDaoImpl; -import com.cloud.network.dao.LBStickinessPolicyDaoImpl; -import com.cloud.network.dao.LoadBalancerDaoImpl; -import com.cloud.network.dao.LoadBalancerVMMapDaoImpl; -import com.cloud.network.dao.NetworkDaoImpl; -import com.cloud.network.dao.NetworkDomainDaoImpl; -import com.cloud.network.dao.NetworkExternalFirewallDaoImpl; -import com.cloud.network.dao.NetworkExternalLoadBalancerDaoImpl; -import com.cloud.network.dao.NetworkRuleConfigDaoImpl; -import com.cloud.network.dao.NetworkServiceMapDaoImpl; -import com.cloud.network.dao.PhysicalNetworkDaoImpl; -import com.cloud.network.dao.PhysicalNetworkServiceProviderDaoImpl; -import com.cloud.network.dao.PhysicalNetworkTrafficTypeDaoImpl; -import com.cloud.network.dao.PortProfileDaoImpl; -import com.cloud.network.dao.RemoteAccessVpnDaoImpl; -import com.cloud.network.dao.Site2SiteCustomerGatewayDaoImpl; -import com.cloud.network.dao.Site2SiteVpnConnectionDaoImpl; -import com.cloud.network.dao.Site2SiteVpnGatewayDaoImpl; -import com.cloud.network.dao.VirtualRouterProviderDaoImpl; -import com.cloud.network.dao.VpnUserDaoImpl; -import com.cloud.network.element.VirtualRouterElement; -import com.cloud.network.element.VirtualRouterElementService; -import com.cloud.network.firewall.FirewallManagerImpl; -import com.cloud.network.lb.LoadBalancingRulesManagerImpl; -import com.cloud.network.router.VpcVirtualNetworkApplianceManagerImpl; -import com.cloud.network.rules.RulesManagerImpl; -import com.cloud.network.rules.dao.PortForwardingRulesDaoImpl; -import com.cloud.network.security.SecurityGroupManagerImpl2; -import com.cloud.network.security.dao.SecurityGroupDaoImpl; -import com.cloud.network.security.dao.SecurityGroupRuleDaoImpl; -import com.cloud.network.security.dao.SecurityGroupRulesDaoImpl; -import com.cloud.network.security.dao.SecurityGroupVMMapDaoImpl; -import com.cloud.network.security.dao.SecurityGroupWorkDaoImpl; -import com.cloud.network.security.dao.VmRulesetLogDaoImpl; -import com.cloud.network.vpc.NetworkACLManagerImpl; -import com.cloud.network.vpc.VpcManagerImpl; -import com.cloud.network.vpc.dao.PrivateIpDaoImpl; -import com.cloud.network.vpc.dao.StaticRouteDaoImpl; -import com.cloud.network.vpc.dao.VpcDaoImpl; -import com.cloud.network.vpc.dao.VpcGatewayDaoImpl; -import com.cloud.network.vpc.dao.VpcOfferingDaoImpl; -import com.cloud.network.vpc.dao.VpcOfferingServiceMapDaoImpl; -import com.cloud.network.vpn.RemoteAccessVpnManagerImpl; -import com.cloud.network.vpn.Site2SiteVpnManagerImpl; -import com.cloud.offerings.dao.NetworkOfferingDaoImpl; -import com.cloud.offerings.dao.NetworkOfferingServiceMapDaoImpl; -import com.cloud.projects.ProjectManagerImpl; -import com.cloud.projects.dao.ProjectAccountDaoImpl; -import com.cloud.projects.dao.ProjectDaoImpl; -import com.cloud.projects.dao.ProjectInvitationDaoImpl; -import com.cloud.resource.ResourceManagerImpl; -import com.cloud.resourcelimit.ResourceLimitManagerImpl; -import com.cloud.service.dao.ServiceOfferingDaoImpl; -import com.cloud.storage.OCFS2ManagerImpl; -import com.cloud.storage.StorageManagerImpl; -import com.cloud.storage.dao.DiskOfferingDaoImpl; -import com.cloud.storage.dao.GuestOSCategoryDaoImpl; -import com.cloud.storage.dao.GuestOSDaoImpl; -import com.cloud.storage.dao.LaunchPermissionDaoImpl; -import com.cloud.storage.dao.S3DaoImpl; -import com.cloud.storage.dao.SnapshotDaoImpl; -import com.cloud.storage.dao.SnapshotPolicyDaoImpl; -import com.cloud.storage.dao.SnapshotScheduleDaoImpl; -import com.cloud.storage.dao.StoragePoolDaoImpl; -import com.cloud.storage.dao.StoragePoolHostDaoImpl; -import com.cloud.storage.dao.StoragePoolWorkDaoImpl; -import com.cloud.storage.dao.SwiftDaoImpl; -import com.cloud.storage.dao.UploadDaoImpl; -import com.cloud.storage.dao.VMTemplateDaoImpl; -import com.cloud.storage.dao.VMTemplateDetailsDaoImpl; -import com.cloud.storage.dao.VMTemplateHostDaoImpl; -import com.cloud.storage.dao.VMTemplatePoolDaoImpl; -import com.cloud.storage.dao.VMTemplateS3DaoImpl; -import com.cloud.storage.dao.VMTemplateSwiftDaoImpl; -import com.cloud.storage.dao.VMTemplateZoneDaoImpl; -import com.cloud.storage.dao.VolumeDaoImpl; -import com.cloud.storage.dao.VolumeHostDaoImpl; -import com.cloud.storage.download.DownloadMonitorImpl; -import com.cloud.storage.s3.S3ManagerImpl; -import com.cloud.storage.secondary.SecondaryStorageManagerImpl; -import com.cloud.storage.snapshot.SnapshotManagerImpl; -import com.cloud.storage.snapshot.SnapshotSchedulerImpl; -import com.cloud.storage.swift.SwiftManagerImpl; -import com.cloud.storage.upload.UploadMonitorImpl; -import com.cloud.tags.TaggedResourceManagerImpl; -import com.cloud.tags.dao.ResourceTagsDaoImpl; -import com.cloud.template.HyervisorTemplateAdapter; -import com.cloud.template.TemplateAdapter; -import com.cloud.template.TemplateAdapter.TemplateAdapterType; -import com.cloud.template.TemplateManagerImpl; -import com.cloud.user.AccountDetailsDaoImpl; -import com.cloud.user.AccountManagerImpl; -import com.cloud.user.DomainManagerImpl; -import com.cloud.user.dao.AccountDaoImpl; -import com.cloud.user.dao.SSHKeyPairDaoImpl; -import com.cloud.user.dao.UserAccountDaoImpl; -import com.cloud.user.dao.UserDaoImpl; -import com.cloud.user.dao.UserStatisticsDaoImpl; -import com.cloud.user.dao.UserStatsLogDaoImpl; -import com.cloud.utils.component.Adapter; -import com.cloud.utils.component.ComponentLibrary; -import com.cloud.utils.component.ComponentLibraryBase; -import com.cloud.utils.component.LegacyComponentLocator.ComponentInfo; -import com.cloud.utils.component.Manager; -import com.cloud.utils.component.PluggableService; -import com.cloud.utils.db.GenericDao; -import com.cloud.uuididentity.IdentityServiceImpl; -import com.cloud.uuididentity.dao.IdentityDaoImpl; -import com.cloud.vm.ClusteredVirtualMachineManagerImpl; -import com.cloud.vm.ItWorkDaoImpl; -import com.cloud.vm.UserVmManagerImpl; -import com.cloud.vm.dao.ConsoleProxyDaoImpl; -import com.cloud.vm.dao.DomainRouterDaoImpl; -import com.cloud.vm.dao.InstanceGroupDaoImpl; -import com.cloud.vm.dao.InstanceGroupVMMapDaoImpl; -import com.cloud.vm.dao.NicDaoImpl; -import com.cloud.vm.dao.SecondaryStorageVmDaoImpl; -import com.cloud.vm.dao.UserVmDaoImpl; -import com.cloud.vm.dao.UserVmDetailsDaoImpl; -import com.cloud.vm.dao.VMInstanceDaoImpl; -import com.cloud.event.dao.EventJoinDaoImpl; - - - -public class DefaultComponentLibrary extends ComponentLibraryBase implements ComponentLibrary { - protected void populateDaos() { - addDao("StackMaidDao", StackMaidDaoImpl.class); - addDao("VMTemplateZoneDao", VMTemplateZoneDaoImpl.class); - addDao("VMTemplateDetailsDao", VMTemplateDetailsDaoImpl.class); - addDao("DomainRouterDao", DomainRouterDaoImpl.class); - addDao("HostDao", HostDaoImpl.class); - addDao("VMInstanceDao", VMInstanceDaoImpl.class); - addDao("UserVmDao", UserVmDaoImpl.class); - ComponentInfo> info = addDao("ServiceOfferingDao", ServiceOfferingDaoImpl.class); - info.addParameter("cache.size", "50"); - info.addParameter("cache.time.to.live", "600"); - info = addDao("DiskOfferingDao", DiskOfferingDaoImpl.class); - info.addParameter("cache.size", "50"); - info.addParameter("cache.time.to.live", "600"); - info = addDao("DataCenterDao", DataCenterDaoImpl.class); - info.addParameter("cache.size", "50"); - info.addParameter("cache.time.to.live", "600"); - info = addDao("HostPodDao", HostPodDaoImpl.class); - info.addParameter("cache.size", "50"); - info.addParameter("cache.time.to.live", "600"); - addDao("IPAddressDao", IPAddressDaoImpl.class); - info = addDao("VlanDao", VlanDaoImpl.class); - info.addParameter("cache.size", "30"); - info.addParameter("cache.time.to.live", "3600"); - addDao("PodVlanMapDao", PodVlanMapDaoImpl.class); - addDao("AccountVlanMapDao", AccountVlanMapDaoImpl.class); - addDao("VolumeDao", VolumeDaoImpl.class); - addDao("EventDao", EventDaoImpl.class); - info = addDao("UserDao", UserDaoImpl.class); - info.addParameter("cache.size", "5000"); - info.addParameter("cache.time.to.live", "300"); - addDao("UserStatisticsDao", UserStatisticsDaoImpl.class); - addDao("UserStatsLogDao", UserStatsLogDaoImpl.class); - addDao("FirewallRulesDao", FirewallRulesDaoImpl.class); - addDao("LoadBalancerDao", LoadBalancerDaoImpl.class); - addDao("NetworkRuleConfigDao", NetworkRuleConfigDaoImpl.class); - addDao("LoadBalancerVMMapDao", LoadBalancerVMMapDaoImpl.class); - addDao("LBStickinessPolicyDao", LBStickinessPolicyDaoImpl.class); - addDao("CounterDao", CounterDaoImpl.class); - addDao("ConditionDao", ConditionDaoImpl.class); - addDao("AutoScalePolicyDao", AutoScalePolicyDaoImpl.class); - addDao("AutoScalePolicyConditionMapDao", AutoScalePolicyConditionMapDaoImpl.class); - addDao("AutoScaleVmProfileDao", AutoScaleVmProfileDaoImpl.class); - addDao("AutoScaleVmGroupDao", AutoScaleVmGroupDaoImpl.class); - addDao("AutoScaleVmGroupPolicyMapDao", AutoScaleVmGroupPolicyMapDaoImpl.class); - addDao("DataCenterIpAddressDao", DataCenterIpAddressDaoImpl.class); - addDao("SecurityGroupDao", SecurityGroupDaoImpl.class); - addDao("SecurityGroupRuleDao", SecurityGroupRuleDaoImpl.class); - addDao("SecurityGroupVMMapDao", SecurityGroupVMMapDaoImpl.class); - addDao("SecurityGroupRulesDao", SecurityGroupRulesDaoImpl.class); - addDao("SecurityGroupWorkDao", SecurityGroupWorkDaoImpl.class); - addDao("VmRulesetLogDao", VmRulesetLogDaoImpl.class); - addDao("AlertDao", AlertDaoImpl.class); - addDao("CapacityDao", CapacityDaoImpl.class); - addDao("DomainDao", DomainDaoImpl.class); - addDao("AccountDao", AccountDaoImpl.class); - addDao("ResourceLimitDao", ResourceLimitDaoImpl.class); - addDao("ResourceCountDao", ResourceCountDaoImpl.class); - addDao("UserAccountDao", UserAccountDaoImpl.class); - addDao("VMTemplateHostDao", VMTemplateHostDaoImpl.class); - addDao("VolumeHostDao", VolumeHostDaoImpl.class); - addDao("VMTemplateSwiftDao", VMTemplateSwiftDaoImpl.class); - addDao("VMTemplateS3Dao", VMTemplateS3DaoImpl.class); - addDao("UploadDao", UploadDaoImpl.class); - addDao("VMTemplatePoolDao", VMTemplatePoolDaoImpl.class); - addDao("LaunchPermissionDao", LaunchPermissionDaoImpl.class); - addDao("ConfigurationDao", ConfigurationDaoImpl.class); - info = addDao("VMTemplateDao", VMTemplateDaoImpl.class); - info.addParameter("cache.size", "100"); - info.addParameter("cache.time.to.live", "600"); - info.addParameter("routing.uniquename", "routing"); - addDao("HighAvailabilityDao", HighAvailabilityDaoImpl.class); - addDao("ConsoleProxyDao", ConsoleProxyDaoImpl.class); - addDao("SecondaryStorageVmDao", SecondaryStorageVmDaoImpl.class); - addDao("ManagementServerHostDao", ManagementServerHostDaoImpl.class); - addDao("ManagementServerHostPeerDao", ManagementServerHostPeerDaoImpl.class); - addDao("AgentUpgradeDao", AgentUpgradeDaoImpl.class); - addDao("SnapshotDao", SnapshotDaoImpl.class); - addDao("AsyncJobDao", AsyncJobDaoImpl.class); - addDao("SyncQueueDao", SyncQueueDaoImpl.class); - addDao("SyncQueueItemDao", SyncQueueItemDaoImpl.class); - addDao("GuestOSDao", GuestOSDaoImpl.class); - addDao("GuestOSCategoryDao", GuestOSCategoryDaoImpl.class); - addDao("StoragePoolDao", StoragePoolDaoImpl.class); - addDao("StoragePoolHostDao", StoragePoolHostDaoImpl.class); - addDao("DetailsDao", HostDetailsDaoImpl.class); - addDao("SnapshotPolicyDao", SnapshotPolicyDaoImpl.class); - addDao("SnapshotScheduleDao", SnapshotScheduleDaoImpl.class); - addDao("ClusterDao", ClusterDaoImpl.class); - addDao("CertificateDao", CertificateDaoImpl.class); - addDao("NetworkConfigurationDao", NetworkDaoImpl.class); - addDao("NetworkOfferingDao", NetworkOfferingDaoImpl.class); - addDao("NicDao", NicDaoImpl.class); - addDao("InstanceGroupDao", InstanceGroupDaoImpl.class); - addDao("InstanceGroupJoinDao", InstanceGroupJoinDaoImpl.class); - addDao("InstanceGroupVMMapDao", InstanceGroupVMMapDaoImpl.class); - addDao("RemoteAccessVpnDao", RemoteAccessVpnDaoImpl.class); - addDao("VpnUserDao", VpnUserDaoImpl.class); - addDao("ItWorkDao", ItWorkDaoImpl.class); - addDao("FirewallRulesDao", FirewallRulesDaoImpl.class); - addDao("PortForwardingRulesDao", PortForwardingRulesDaoImpl.class); - addDao("FirewallRulesCidrsDao", FirewallRulesCidrsDaoImpl.class); - addDao("SSHKeyPairDao", SSHKeyPairDaoImpl.class); - addDao("UsageEventDao", UsageEventDaoImpl.class); - addDao("ClusterDetailsDao", ClusterDetailsDaoImpl.class); - addDao("UserVmDetailsDao", UserVmDetailsDaoImpl.class); - addDao("StoragePoolWorkDao", StoragePoolWorkDaoImpl.class); - addDao("HostTagsDao", HostTagsDaoImpl.class); - addDao("NetworkDomainDao", NetworkDomainDaoImpl.class); - addDao("KeystoreDao", KeystoreDaoImpl.class); - addDao("DcDetailsDao", DcDetailsDaoImpl.class); - addDao("SwiftDao", SwiftDaoImpl.class); - addDao("S3Dao", S3DaoImpl.class); - addDao("AgentTransferMapDao", HostTransferMapDaoImpl.class); - addDao("ProjectDao", ProjectDaoImpl.class); - addDao("InlineLoadBalancerNicMapDao", InlineLoadBalancerNicMapDaoImpl.class); - addDao("ProjectsAccountDao", ProjectAccountDaoImpl.class); - addDao("ProjectInvitationDao", ProjectInvitationDaoImpl.class); - addDao("IdentityDao", IdentityDaoImpl.class); - addDao("AccountDetailsDao", AccountDetailsDaoImpl.class); - addDao("NetworkOfferingServiceMapDao", NetworkOfferingServiceMapDaoImpl.class); - info = addDao("HypervisorCapabilitiesDao",HypervisorCapabilitiesDaoImpl.class); - info.addParameter("cache.size", "100"); - info.addParameter("cache.time.to.live", "600"); - addDao("PhysicalNetworkDao", PhysicalNetworkDaoImpl.class); - addDao("PhysicalNetworkServiceProviderDao", PhysicalNetworkServiceProviderDaoImpl.class); - addDao("VirtualRouterProviderDao", VirtualRouterProviderDaoImpl.class); - addDao("ExternalLoadBalancerDeviceDao", ExternalLoadBalancerDeviceDaoImpl.class); - addDao("ExternalFirewallDeviceDao", ExternalFirewallDeviceDaoImpl.class); - addDao("NetworkExternalLoadBalancerDao", NetworkExternalLoadBalancerDaoImpl.class); - addDao("NetworkExternalFirewallDao", NetworkExternalFirewallDaoImpl.class); - addDao("ClusterVSMMapDao", ClusterVSMMapDaoImpl.class); - addDao("PortProfileDao", PortProfileDaoImpl.class); - addDao("PhysicalNetworkTrafficTypeDao", PhysicalNetworkTrafficTypeDaoImpl.class); - addDao("NetworkServiceMapDao", NetworkServiceMapDaoImpl.class); - addDao("StorageNetworkIpAddressDao", StorageNetworkIpAddressDaoImpl.class); - addDao("StorageNetworkIpRangeDao", StorageNetworkIpRangeDaoImpl.class); - addDao("VpcDao", VpcDaoImpl.class); - addDao("VpcOfferingDao", VpcOfferingDaoImpl.class); - addDao("VpcOfferingServiceMapDao", VpcOfferingServiceMapDaoImpl.class); - addDao("PrivateIpDao", PrivateIpDaoImpl.class); - addDao("VpcGatewayDao", VpcGatewayDaoImpl.class); - addDao("StaticRouteDao", StaticRouteDaoImpl.class); - addDao("TagsDao", ResourceTagsDaoImpl.class); - addDao("Site2SiteVpnGatewayDao", Site2SiteVpnGatewayDaoImpl.class); - addDao("Site2SiteCustomerGatewayDao", Site2SiteCustomerGatewayDaoImpl.class); - addDao("Site2SiteVpnConnnectionDao", Site2SiteVpnConnectionDaoImpl.class); - - addDao("UserVmJoinDao", UserVmJoinDaoImpl.class); - addDao("DomainRouterJoinDao", DomainRouterJoinDaoImpl.class); - addDao("SecurityGroupJoinDao", SecurityGroupJoinDaoImpl.class); - addDao("ResourceTagJoinDao", ResourceTagJoinDaoImpl.class); - addDao("EventJoinDao", EventJoinDaoImpl.class); - addDao("UserAccountJoinDao", UserAccountJoinDaoImpl.class); - addDao("ProjectJoinDao", ProjectJoinDaoImpl.class); - addDao("ProjectAccountJoinDao", ProjectAccountJoinDaoImpl.class); - addDao("ProjectInvitationJoinDao", ProjectInvitationJoinDaoImpl.class); - addDao("HostJoinDao", HostJoinDaoImpl.class); - addDao("VolumeJoinDao", VolumeJoinDaoImpl.class); - addDao("AccountJoinDao", AccountJoinDaoImpl.class); - addDao("AsyncJobJoinDao", AsyncJobJoinDaoImpl.class); - addDao("StoragePoolJoinDao", StoragePoolJoinDaoImpl.class); - } - - @Override - public synchronized Map>> getDaos() { - if (_daos.size() == 0) { - populateDaos(); - } - //FIXME: Incorrect method return definition - return _daos; - } - - protected void populateManagers() { - addManager("StackMaidManager", CheckPointManagerImpl.class); - addManager("Cluster Manager", ClusterManagerImpl.class); - addManager("ClusterFenceManager", ClusterFenceManagerImpl.class); - addManager("ClusteredAgentManager", ClusteredAgentManagerImpl.class); - addManager("SyncQueueManager", SyncQueueManagerImpl.class); - addManager("AsyncJobManager", AsyncJobManagerImpl.class); - addManager("AsyncJobExecutorContext", AsyncJobExecutorContextImpl.class); - addManager("configuration manager", ConfigurationManagerImpl.class); - addManager("account manager", AccountManagerImpl.class); - addManager("domain manager", DomainManagerImpl.class); - addManager("resource limit manager", ResourceLimitManagerImpl.class); - addManager("network manager", NetworkManagerImpl.class); - addManager("download manager", DownloadMonitorImpl.class); - addManager("upload manager", UploadMonitorImpl.class); - addManager("keystore manager", KeystoreManagerImpl.class); - addManager("secondary storage vm manager", SecondaryStorageManagerImpl.class); - addManager("vm manager", UserVmManagerImpl.class); - addManager("upgrade manager", UpgradeManagerImpl.class); - addManager("StorageManager", StorageManagerImpl.class); - addManager("Alert Manager", AlertManagerImpl.class); - addManager("Template Manager", TemplateManagerImpl.class); - addManager("Snapshot Manager", SnapshotManagerImpl.class); - addManager("SnapshotScheduler", SnapshotSchedulerImpl.class); - addManager("SecurityGroupManager", SecurityGroupManagerImpl2.class); - addManager("EntityManager", EntityManagerImpl.class); - addManager("LoadBalancingRulesManager", LoadBalancingRulesManagerImpl.class); - addManager("AutoScaleManager", AutoScaleManagerImpl.class); - addManager("RulesManager", RulesManagerImpl.class); - addManager("RemoteAccessVpnManager", RemoteAccessVpnManagerImpl.class); - addManager("Capacity Manager", CapacityManagerImpl.class); - addManager("VirtualMachineManager", ClusteredVirtualMachineManagerImpl.class); - addManager("HypervisorGuruManager", HypervisorGuruManagerImpl.class); - addManager("ResourceManager", ResourceManagerImpl.class); - addManager("IdentityManager", IdentityServiceImpl.class); - addManager("OCFS2Manager", OCFS2ManagerImpl.class); - addManager("FirewallManager", FirewallManagerImpl.class); - ComponentInfo info = addManager("ConsoleProxyManager", ConsoleProxyManagerImpl.class); - info.addParameter("consoleproxy.sslEnabled", "true"); - addManager("ProjectManager", ProjectManagerImpl.class); - addManager("SwiftManager", SwiftManagerImpl.class); - addManager("S3Manager", S3ManagerImpl.class); - addManager("StorageNetworkManager", StorageNetworkManagerImpl.class); - addManager("ExternalLoadBalancerUsageManager", ExternalLoadBalancerUsageManagerImpl.class); - addManager("HA Manager", HighAvailabilityManagerImpl.class); - addManager("VPC Manager", VpcManagerImpl.class); - addManager("VpcVirtualRouterManager", VpcVirtualNetworkApplianceManagerImpl.class); - addManager("NetworkACLManager", NetworkACLManagerImpl.class); - addManager("TaggedResourcesManager", TaggedResourceManagerImpl.class); - addManager("Site2SiteVpnManager", Site2SiteVpnManagerImpl.class); - addManager("QueryManager", QueryManagerImpl.class); - } - - @Override - public synchronized Map> getManagers() { - if (_managers.size() == 0) { - populateManagers(); - } - return _managers; - } - - protected void populateAdapters() { - addAdapter(TemplateAdapter.class, TemplateAdapterType.Hypervisor.getName(), HyervisorTemplateAdapter.class); - } - - @Override - public synchronized Map>> getAdapters() { - if (_adapters.size() == 0) { - populateAdapters(); - } - return _adapters; - } - - @Override - public synchronized Map, Class> getFactories() { - HashMap, Class> factories = new HashMap, Class>(); - factories.put(EntityManager.class, EntityManagerImpl.class); - return factories; - } - - protected void populateServices() { - addService("VirtualRouterElementService", VirtualRouterElementService.class, VirtualRouterElement.class); - } - - @Override - public synchronized Map> getPluggableServices() { - if (_pluggableServices.size() == 0) { - populateServices(); - } - return _pluggableServices; - } -} diff --git a/server/src/com/cloud/configuration/PremiumComponentLibrary.java b/server/src/com/cloud/configuration/PremiumComponentLibrary.java deleted file mode 100755 index b25f462f4d0..00000000000 --- a/server/src/com/cloud/configuration/PremiumComponentLibrary.java +++ /dev/null @@ -1,72 +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. -package com.cloud.configuration; - -import java.util.ArrayList; -import java.util.List; - -import com.cloud.baremetal.BareMetalPingServiceImpl; -import com.cloud.baremetal.BareMetalTemplateAdapter; -import com.cloud.baremetal.BareMetalVmManagerImpl; -import com.cloud.baremetal.ExternalDhcpManagerImpl; -import com.cloud.baremetal.PxeServerManager.PxeServerType; -import com.cloud.baremetal.PxeServerManagerImpl; -import com.cloud.baremetal.PxeServerService; -import com.cloud.ha.HighAvailabilityManagerExtImpl; -import com.cloud.network.ExternalNetworkDeviceManagerImpl; -import com.cloud.network.NetworkUsageManagerImpl; -import com.cloud.secstorage.CommandExecLogDaoImpl; -import com.cloud.secstorage.PremiumSecondaryStorageManagerImpl; -import com.cloud.template.TemplateAdapter; -import com.cloud.template.TemplateAdapter.TemplateAdapterType; -import com.cloud.upgrade.PremiumDatabaseUpgradeChecker; -import com.cloud.usage.dao.UsageDaoImpl; -import com.cloud.usage.dao.UsageIPAddressDaoImpl; -import com.cloud.usage.dao.UsageJobDaoImpl; -import com.cloud.utils.component.SystemIntegrityChecker; - -public class PremiumComponentLibrary extends DefaultComponentLibrary { - @Override - protected void populateDaos() { - super.populateDaos(); - addDao("UsageJobDao", UsageJobDaoImpl.class); - addDao("UsageDao", UsageDaoImpl.class); - addDao("UsageIpAddressDao", UsageIPAddressDaoImpl.class); - addDao("CommandExecLogDao", CommandExecLogDaoImpl.class); - } - - @Override - protected void populateManagers() { - // override FOSS SSVM manager - super.populateManagers(); - addManager("secondary storage vm manager", PremiumSecondaryStorageManagerImpl.class); - - addManager("HA Manager", HighAvailabilityManagerExtImpl.class); - addManager("ExternalNetworkManager", ExternalNetworkDeviceManagerImpl.class); - addManager("BareMetalVmManager", BareMetalVmManagerImpl.class); - addManager("ExternalDhcpManager", ExternalDhcpManagerImpl.class); - addManager("PxeServerManager", PxeServerManagerImpl.class); - addManager("NetworkUsageManager", NetworkUsageManagerImpl.class); - } - - @Override - protected void populateAdapters() { - super.populateAdapters(); - addAdapter(PxeServerService.class, PxeServerType.PING.getName(), BareMetalPingServiceImpl.class); - addAdapter(TemplateAdapter.class, TemplateAdapterType.BareMetal.getName(), BareMetalTemplateAdapter.class); - } -} diff --git a/server/src/com/cloud/server/CloudStackComponentComposer.java b/server/src/com/cloud/server/CloudStackComponentComposer.java index 6bd9bc5db45..ae063259a53 100644 --- a/server/src/com/cloud/server/CloudStackComponentComposer.java +++ b/server/src/com/cloud/server/CloudStackComponentComposer.java @@ -30,7 +30,6 @@ import com.cloud.api.query.QueryManagerImpl; import com.cloud.async.AsyncJobManager; import com.cloud.async.SyncQueueManager; import com.cloud.capacity.CapacityManagerImpl; -import com.cloud.cluster.CheckPointManagerImpl; import com.cloud.cluster.ClusterFenceManagerImpl; import com.cloud.cluster.ClusterManagerImpl; import com.cloud.configuration.ConfigurationManager; @@ -75,7 +74,7 @@ import com.cloud.vm.VirtualMachineManager; @Component public class CloudStackComponentComposer { - @Inject CheckPointManagerImpl _checkPointMgr; + // @Inject CheckPointManagerImpl _checkPointMgr; @Inject ClusterManagerImpl _clusterMgr; @Inject ClusterFenceManagerImpl _clusterFenceMgr; @Inject AgentManager _AgentMgr; @@ -130,7 +129,7 @@ public class CloudStackComponentComposer { @PostConstruct void init() { - _managers.add(_checkPointMgr); + // _managers.add(_checkPointMgr); _managers.add(_clusterMgr); _managers.add(_clusterFenceMgr); _managers.add(_AgentMgr); diff --git a/server/src/com/cloud/servlet/ConsoleProxyServlet.java b/server/src/com/cloud/servlet/ConsoleProxyServlet.java index 568ad0638c6..6a47ba28642 100644 --- a/server/src/com/cloud/servlet/ConsoleProxyServlet.java +++ b/server/src/com/cloud/servlet/ConsoleProxyServlet.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.PostConstruct; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.inject.Inject; @@ -36,6 +37,7 @@ import javax.servlet.http.HttpSession; import org.apache.cloudstack.api.IdentityService; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.exception.PermissionDeniedException; import com.cloud.host.HostVO; @@ -57,6 +59,7 @@ import com.cloud.vm.VirtualMachineManager; * Console access : /conosole?cmd=access&vm=xxx * Authentication : /console?cmd=auth&vm=xxx&sid=xxx */ +@Component("consoleServlet") public class ConsoleProxyServlet extends HttpServlet { private static final long serialVersionUID = -5515382620323808168L; public static final Logger s_logger = Logger.getLogger(ConsoleProxyServlet.class.getName()); @@ -68,6 +71,15 @@ public class ConsoleProxyServlet extends HttpServlet { @Inject ManagementServer _ms; @Inject IdentityService _identityService; + static ManagementServer s_ms; + public ConsoleProxyServlet() { + } + + @PostConstruct + void initComponent() { + s_ms = _ms; + } + @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) { doGet(req, resp); @@ -398,7 +410,7 @@ public class ConsoleProxyServlet extends HttpServlet { long ts = normalizedHashTime.getTime(); ts = ts/60000; // round up to 1 minute - String secretKey = _ms.getHashKey(); + String secretKey = s_ms.getHashKey(); SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1"); mac.init(keySpec); diff --git a/server/src/com/cloud/servlet/RegisterCompleteServlet.java b/server/src/com/cloud/servlet/RegisterCompleteServlet.java index aecbce39f92..59224553b35 100644 --- a/server/src/com/cloud/servlet/RegisterCompleteServlet.java +++ b/server/src/com/cloud/servlet/RegisterCompleteServlet.java @@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.configuration.Configuration; import com.cloud.configuration.dao.ConfigurationDao; @@ -37,7 +38,7 @@ import com.cloud.user.UserVO; import com.cloud.user.dao.UserDao; import com.cloud.utils.SerialVersionUID; - +@Component("registerCompleteServlet") public class RegisterCompleteServlet extends HttpServlet implements ServletContextListener { public static final Logger s_logger = Logger.getLogger(RegisterCompleteServlet.class.getName()); diff --git a/server/src/com/cloud/test/DatabaseConfig.java b/server/src/com/cloud/test/DatabaseConfig.java index f0e9d82ea8d..7c10f98abf4 100755 --- a/server/src/com/cloud/test/DatabaseConfig.java +++ b/server/src/com/cloud/test/DatabaseConfig.java @@ -359,7 +359,7 @@ public class DatabaseConfig { s_logger.error("error starting database config, missing initial data file"); } else { try { - DatabaseConfig config = ComponentContext.inject(DatabaseConfig.class, args[0]); + DatabaseConfig config = ComponentContext.inject(DatabaseConfig.class); config.doVersionCheck(); config.doConfig(); System.exit(0); diff --git a/server/test/com/cloud/async/TestAsyncJobManager.java b/server/test/com/cloud/async/TestAsyncJobManager.java index d45ca4dc54a..7cb24ddc388 100644 --- a/server/test/com/cloud/async/TestAsyncJobManager.java +++ b/server/test/com/cloud/async/TestAsyncJobManager.java @@ -200,12 +200,12 @@ public class TestAsyncJobManager extends TestCase { if(host != null) { s_logger.info("Thread " + (current + 1) + " acquired lock"); - try { Thread.sleep(getRandomMilliseconds(1000, 5000)); } catch (InterruptedException e) {} + try { Thread.sleep(1000); } catch (InterruptedException e) {} s_logger.info("Thread " + (current + 1) + " released lock"); hostDao.releaseFromLockTable(host.getId()); - try { Thread.sleep(getRandomMilliseconds(1000, 5000)); } catch (InterruptedException e) {} + try { Thread.sleep(1000); } catch (InterruptedException e) {} } else { s_logger.info("Thread " + (current + 1) + " is not able to acquire lock"); } @@ -226,7 +226,6 @@ public class TestAsyncJobManager extends TestCase { } public void testDomain() { - getRandomMilliseconds(1, 100); DomainDao domainDao = new DomainDaoImpl(); DomainVO domain1 = new DomainVO("d1", 2L, 1L, null); diff --git a/server/test/com/cloud/async/TestSyncQueueManager.java b/server/test/com/cloud/async/TestSyncQueueManager.java index 32b7ddeb7d8..2322aecd332 100644 --- a/server/test/com/cloud/async/TestSyncQueueManager.java +++ b/server/test/com/cloud/async/TestSyncQueueManager.java @@ -67,7 +67,7 @@ public class TestSyncQueueManager extends TestCase { mgr.purgeItem(item.getId()); } try { - Thread.sleep(getRandomMilliseconds(1, 10)); + Thread.sleep(100); } catch (InterruptedException e) { } } @@ -90,7 +90,7 @@ public class TestSyncQueueManager extends TestCase { } try { - Thread.sleep(getRandomMilliseconds(1, 10)); + Thread.sleep(100); } catch (InterruptedException e) { } } @@ -138,7 +138,7 @@ public class TestSyncQueueManager extends TestCase { } } try { - Thread.sleep(getRandomMilliseconds(1, 10)); + Thread.sleep(100); } catch (InterruptedException e) { } } @@ -162,7 +162,7 @@ public class TestSyncQueueManager extends TestCase { } try { - Thread.sleep(getRandomMilliseconds(1, 10)); + Thread.sleep(100); } catch (InterruptedException e) { } } diff --git a/server/test/com/cloud/network/security/SecurityGroupManagerImpl2Test.java b/server/test/com/cloud/network/security/SecurityGroupManagerImpl2Test.java index 6bf94a2ef95..723e4e6ee27 100644 --- a/server/test/com/cloud/network/security/SecurityGroupManagerImpl2Test.java +++ b/server/test/com/cloud/network/security/SecurityGroupManagerImpl2Test.java @@ -19,6 +19,7 @@ package com.cloud.network.security; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; import javax.naming.ConfigurationException; import junit.framework.TestCase; @@ -47,7 +48,6 @@ import com.cloud.user.MockDomainManagerImpl; import com.cloud.user.dao.AccountDaoImpl; import com.cloud.utils.Profiler; -import com.cloud.utils.component.MockComponentLocator; import com.cloud.vm.MockUserVmManagerImpl; import com.cloud.vm.MockVirtualMachineManagerImpl; import com.cloud.vm.dao.UserVmDaoImpl; @@ -55,14 +55,13 @@ import com.cloud.vm.dao.VMInstanceDaoImpl; public class SecurityGroupManagerImpl2Test extends TestCase { //private final static Logger s_logger = Logger.getLogger(SecurityGroupManagerImpl2Test.class); - SecurityGroupManagerImpl2 _sgMgr = null; - UserVmDaoImpl _vmDao = null; + @Inject SecurityGroupManagerImpl2 _sgMgr = null; + @Inject UserVmDaoImpl _vmDao = null; @Before @Override public void setUp() { - MockComponentLocator locator = new MockComponentLocator("management-server"); - +/* locator.addDao("ConfigurationDao", ConfigurationDaoImpl.class); locator.addDao("SecurityGroupDao", SecurityGroupDaoImpl.class); @@ -87,8 +86,7 @@ public class SecurityGroupManagerImpl2Test extends TestCase { locator.addManager("DomainManager", MockDomainManagerImpl.class); locator.addManager("ProjectManager", MockProjectManagerImpl.class); locator.makeActive(new DefaultInterceptorLibrary()); - _sgMgr = ComponentLocator.inject(SecurityGroupManagerImpl2.class); - _sgMgr._mBean = new SecurityManagerMBeanImpl(_sgMgr); +*/ } @Override diff --git a/server/test/com/cloud/snapshot/SnapshotDaoTest.java b/server/test/com/cloud/snapshot/SnapshotDaoTest.java index 644980b6931..2f2803e067e 100644 --- a/server/test/com/cloud/snapshot/SnapshotDaoTest.java +++ b/server/test/com/cloud/snapshot/SnapshotDaoTest.java @@ -18,6 +18,8 @@ package com.cloud.snapshot; import java.util.List; +import javax.inject.Inject; + import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; import com.cloud.storage.dao.SnapshotDaoImpl; @@ -27,10 +29,9 @@ import junit.framework.Assert; import junit.framework.TestCase; public class SnapshotDaoTest extends TestCase { - + @Inject SnapshotDaoImpl dao; + public void testListBy() { - SnapshotDaoImpl dao = ComponentLocator.inject(SnapshotDaoImpl.class); - List snapshots = dao.listByInstanceId(3, Snapshot.Status.BackedUp); for(SnapshotVO snapshot : snapshots) { Assert.assertTrue(snapshot.getStatus() == Snapshot.Status.BackedUp); diff --git a/server/test/com/cloud/storage/dao/StoragePoolDaoTest.java b/server/test/com/cloud/storage/dao/StoragePoolDaoTest.java index 57d74f682f9..87dbf168ef9 100644 --- a/server/test/com/cloud/storage/dao/StoragePoolDaoTest.java +++ b/server/test/com/cloud/storage/dao/StoragePoolDaoTest.java @@ -16,15 +16,17 @@ // under the License. package com.cloud.storage.dao; +import javax.inject.Inject; + import junit.framework.TestCase; import com.cloud.storage.StoragePoolStatus; public class StoragePoolDaoTest extends TestCase { + @Inject StoragePoolDaoImpl dao; public void testCountByStatus() { - StoragePoolDaoImpl dao = ComponentLocator.inject(StoragePoolDaoImpl.class); long count = dao.countPoolsByStatus(StoragePoolStatus.Up); System.out.println("Found " + count + " storage pools"); } diff --git a/server/test/com/cloud/upgrade/AdvanceZone217To224UpgradeTest.java b/server/test/com/cloud/upgrade/AdvanceZone217To224UpgradeTest.java index 9d442dcf4fa..532a62f3cba 100644 --- a/server/test/com/cloud/upgrade/AdvanceZone217To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/AdvanceZone217To224UpgradeTest.java @@ -22,6 +22,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -35,7 +37,9 @@ import com.cloud.utils.db.Transaction; public class AdvanceZone217To224UpgradeTest extends TestCase { private static final Logger s_logger = Logger.getLogger(AdvanceZone217To224UpgradeTest.class); - + @Inject VersionDaoImpl dao; + @Inject DatabaseUpgradeChecker checker; + @Override @Before public void setUp() throws Exception { @@ -54,9 +58,6 @@ public class AdvanceZone217To224UpgradeTest extends TestCase { Connection conn; PreparedStatement pstmt; - VersionDaoImpl dao = ComponentLocator.inject(VersionDaoImpl.class); - DatabaseUpgradeChecker checker = ComponentLocator.inject(DatabaseUpgradeChecker.class); - String version = dao.getCurrentVersion(); assert version.equals("2.1.7") : "Version returned is not 2.1.7 but " + version; diff --git a/server/test/com/cloud/upgrade/AdvanceZone223To224UpgradeTest.java b/server/test/com/cloud/upgrade/AdvanceZone223To224UpgradeTest.java index 17f3158d324..519ae704c91 100644 --- a/server/test/com/cloud/upgrade/AdvanceZone223To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/AdvanceZone223To224UpgradeTest.java @@ -18,6 +18,8 @@ package com.cloud.upgrade; import java.sql.SQLException; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -29,6 +31,8 @@ import com.cloud.upgrade.dao.VersionDaoImpl; public class AdvanceZone223To224UpgradeTest extends TestCase { private static final Logger s_logger = Logger.getLogger(AdvanceZone223To224UpgradeTest.class); + @Inject VersionDaoImpl dao; + @Inject DatabaseUpgradeChecker checker; @Override @Before @@ -43,8 +47,6 @@ public class AdvanceZone223To224UpgradeTest extends TestCase { public void test223to224Upgrade() throws SQLException { - VersionDaoImpl dao = ComponentLocator.inject(VersionDaoImpl.class); - DatabaseUpgradeChecker checker = ComponentLocator.inject(DatabaseUpgradeChecker.class); String version = dao.getCurrentVersion(); assert version.equals("2.2.3") : "Version returned is not 2.2.3 but " + version; diff --git a/server/test/com/cloud/upgrade/BasicZone218To224UpgradeTest.java b/server/test/com/cloud/upgrade/BasicZone218To224UpgradeTest.java index 19048e0c923..8bd9f0625ef 100644 --- a/server/test/com/cloud/upgrade/BasicZone218To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/BasicZone218To224UpgradeTest.java @@ -22,6 +22,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -35,6 +37,9 @@ import com.cloud.utils.db.Transaction; public class BasicZone218To224UpgradeTest extends TestCase { private static final Logger s_logger = Logger.getLogger(BasicZone218To224UpgradeTest.class); + + @Inject VersionDaoImpl dao; + @Inject DatabaseUpgradeChecker checker; @Override @Before @@ -54,9 +59,6 @@ public class BasicZone218To224UpgradeTest extends TestCase { Connection conn = Transaction.getStandaloneConnection(); PreparedStatement pstmt; - VersionDaoImpl dao = ComponentLocator.inject(VersionDaoImpl.class); - DatabaseUpgradeChecker checker = ComponentLocator.inject(DatabaseUpgradeChecker.class); - String version = dao.getCurrentVersion(); if (!version.equals("2.1.8")) { diff --git a/server/test/com/cloud/upgrade/HostCapacity218to22Test.java b/server/test/com/cloud/upgrade/HostCapacity218to22Test.java index 3d89f219520..76ad12eeb19 100644 --- a/server/test/com/cloud/upgrade/HostCapacity218to22Test.java +++ b/server/test/com/cloud/upgrade/HostCapacity218to22Test.java @@ -18,6 +18,8 @@ package com.cloud.upgrade; import java.sql.SQLException; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -30,6 +32,9 @@ import com.cloud.utils.db.DbTestUtils; public class HostCapacity218to22Test extends TestCase { private static final Logger s_logger = Logger.getLogger(HostCapacity218to22Test.class); + + @Inject VersionDaoImpl dao; + @Inject DatabaseUpgradeChecker checker; @Override @Before @@ -46,9 +51,6 @@ public class HostCapacity218to22Test extends TestCase { s_logger.debug("Finding sample data from 2.1.8"); DbTestUtils.executeScript("fake.sql", false, true); - VersionDaoImpl dao = ComponentLocator.inject(VersionDaoImpl.class); - DatabaseUpgradeChecker checker = ComponentLocator.inject(DatabaseUpgradeChecker.class); - String version = dao.getCurrentVersion(); if (!version.equals("2.1.8")) { diff --git a/server/test/com/cloud/upgrade/InstanceGroup218To224UpgradeTest.java b/server/test/com/cloud/upgrade/InstanceGroup218To224UpgradeTest.java index 8624019e0c7..41f334dab6a 100644 --- a/server/test/com/cloud/upgrade/InstanceGroup218To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/InstanceGroup218To224UpgradeTest.java @@ -23,6 +23,8 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -37,6 +39,9 @@ import com.cloud.utils.db.Transaction; public class InstanceGroup218To224UpgradeTest extends TestCase { private static final Logger s_logger = Logger.getLogger(InstanceGroup218To224UpgradeTest.class); + @Inject VersionDaoImpl dao; + @Inject DatabaseUpgradeChecker checker; + @Override @Before public void setUp() throws Exception { @@ -55,9 +60,6 @@ public class InstanceGroup218To224UpgradeTest extends TestCase { PreparedStatement pstmt; ResultSet rs; - VersionDaoImpl dao = ComponentLocator.inject(VersionDaoImpl.class); - DatabaseUpgradeChecker checker = ComponentLocator.inject(DatabaseUpgradeChecker.class); - String version = dao.getCurrentVersion(); if (!version.equals("2.1.8")) { diff --git a/server/test/com/cloud/upgrade/PortForwarding218To224UpgradeTest.java b/server/test/com/cloud/upgrade/PortForwarding218To224UpgradeTest.java index 647d2b07dfc..a9cb51fe00c 100644 --- a/server/test/com/cloud/upgrade/PortForwarding218To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/PortForwarding218To224UpgradeTest.java @@ -22,6 +22,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -35,6 +37,9 @@ import com.cloud.utils.db.Transaction; public class PortForwarding218To224UpgradeTest extends TestCase { private static final Logger s_logger = Logger.getLogger(PortForwarding218To224UpgradeTest.class); + + @Inject VersionDaoImpl dao; + @Inject DatabaseUpgradeChecker checker; @Override @Before @@ -55,9 +60,6 @@ public class PortForwarding218To224UpgradeTest extends TestCase { PreparedStatement pstmt; ResultSet rs; - VersionDaoImpl dao = ComponentLocator.inject(VersionDaoImpl.class); - DatabaseUpgradeChecker checker = ComponentLocator.inject(DatabaseUpgradeChecker.class); - String version = dao.getCurrentVersion(); if (!version.equals("2.1.8")) { diff --git a/server/test/com/cloud/upgrade/Sanity220To224UpgradeTest.java b/server/test/com/cloud/upgrade/Sanity220To224UpgradeTest.java index 080482fa6f8..d33192fbf9c 100644 --- a/server/test/com/cloud/upgrade/Sanity220To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/Sanity220To224UpgradeTest.java @@ -21,6 +21,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -35,6 +37,9 @@ import com.cloud.utils.db.Transaction; public class Sanity220To224UpgradeTest extends TestCase { private static final Logger s_logger = Logger.getLogger(Sanity220To224UpgradeTest.class); + @Inject VersionDaoImpl dao; + @Inject DatabaseUpgradeChecker checker; + @Override @Before public void setUp() throws Exception { @@ -54,9 +59,6 @@ public class Sanity220To224UpgradeTest extends TestCase { PreparedStatement pstmt; ResultSet rs; - VersionDaoImpl dao = ComponentLocator.inject(VersionDaoImpl.class); - DatabaseUpgradeChecker checker = ComponentLocator.inject(DatabaseUpgradeChecker.class); - String version = dao.getCurrentVersion(); if (!version.equals("2.2.1")) { diff --git a/server/test/com/cloud/upgrade/Sanity222To224UpgradeTest.java b/server/test/com/cloud/upgrade/Sanity222To224UpgradeTest.java index 44e90eb05c8..108eca919a6 100644 --- a/server/test/com/cloud/upgrade/Sanity222To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/Sanity222To224UpgradeTest.java @@ -21,6 +21,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -35,6 +37,9 @@ import com.cloud.utils.db.Transaction; public class Sanity222To224UpgradeTest extends TestCase { private static final Logger s_logger = Logger.getLogger(Sanity222To224UpgradeTest.class); + @Inject VersionDaoImpl dao; + @Inject DatabaseUpgradeChecker checker; + @Override @Before public void setUp() throws Exception { @@ -54,8 +59,6 @@ public class Sanity222To224UpgradeTest extends TestCase { PreparedStatement pstmt; ResultSet rs; - VersionDaoImpl dao = ComponentLocator.inject(VersionDaoImpl.class); - DatabaseUpgradeChecker checker = ComponentLocator.inject(DatabaseUpgradeChecker.class); String version = dao.getCurrentVersion(); diff --git a/server/test/com/cloud/upgrade/Sanity223To225UpgradeTest.java b/server/test/com/cloud/upgrade/Sanity223To225UpgradeTest.java index 7312cc69894..fd0b219af7e 100644 --- a/server/test/com/cloud/upgrade/Sanity223To225UpgradeTest.java +++ b/server/test/com/cloud/upgrade/Sanity223To225UpgradeTest.java @@ -21,6 +21,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -34,6 +36,9 @@ import com.cloud.utils.db.Transaction; public class Sanity223To225UpgradeTest extends TestCase { private static final Logger s_logger = Logger.getLogger(Sanity223To225UpgradeTest.class); + @Inject VersionDaoImpl dao; + @Inject DatabaseUpgradeChecker checker; + @Override @Before public void setUp() throws Exception { @@ -53,9 +58,6 @@ public class Sanity223To225UpgradeTest extends TestCase { PreparedStatement pstmt; ResultSet rs; - VersionDaoImpl dao = ComponentLocator.inject(VersionDaoImpl.class); - DatabaseUpgradeChecker checker = ComponentLocator.inject(DatabaseUpgradeChecker.class); - String version = dao.getCurrentVersion(); if (!version.equals("2.2.3")) { diff --git a/server/test/com/cloud/upgrade/Sanity224To225UpgradeTest.java b/server/test/com/cloud/upgrade/Sanity224To225UpgradeTest.java index 89ab58b4911..775a62ee501 100644 --- a/server/test/com/cloud/upgrade/Sanity224To225UpgradeTest.java +++ b/server/test/com/cloud/upgrade/Sanity224To225UpgradeTest.java @@ -21,6 +21,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -35,6 +37,9 @@ import com.cloud.utils.db.Transaction; public class Sanity224To225UpgradeTest extends TestCase { private static final Logger s_logger = Logger.getLogger(Sanity224To225UpgradeTest.class); + @Inject VersionDaoImpl dao; + @Inject DatabaseUpgradeChecker checker; + @Override @Before public void setUp() throws Exception { @@ -54,9 +59,6 @@ public class Sanity224To225UpgradeTest extends TestCase { PreparedStatement pstmt; ResultSet rs; - VersionDaoImpl dao = ComponentLocator.inject(VersionDaoImpl.class); - DatabaseUpgradeChecker checker = ComponentLocator.inject(DatabaseUpgradeChecker.class); - String version = dao.getCurrentVersion(); if (!version.equals("2.2.4")) { diff --git a/server/test/com/cloud/upgrade/Template2214To30UpgradeTest.java b/server/test/com/cloud/upgrade/Template2214To30UpgradeTest.java index 88a4ea4dcd1..06835b56774 100644 --- a/server/test/com/cloud/upgrade/Template2214To30UpgradeTest.java +++ b/server/test/com/cloud/upgrade/Template2214To30UpgradeTest.java @@ -23,6 +23,8 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -37,6 +39,7 @@ import com.cloud.utils.exception.CloudRuntimeException; public class Template2214To30UpgradeTest extends TestCase { private static final Logger s_logger = Logger .getLogger(Template2214To30UpgradeTest.class); + @Inject DatabaseUpgradeChecker checker; @Override @Before @@ -56,8 +59,6 @@ public class Template2214To30UpgradeTest extends TestCase { "fake.sql", false, true); - DatabaseUpgradeChecker checker = ComponentLocator - .inject(DatabaseUpgradeChecker.class); checker.upgrade("2.2.14", "3.0.0"); diff --git a/server/test/com/cloud/upgrade/Test2214To30DBUpgrade.java b/server/test/com/cloud/upgrade/Test2214To30DBUpgrade.java index 66a2fda7c20..ff448033764 100644 --- a/server/test/com/cloud/upgrade/Test2214To30DBUpgrade.java +++ b/server/test/com/cloud/upgrade/Test2214To30DBUpgrade.java @@ -23,6 +23,8 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -34,10 +36,13 @@ import com.cloud.utils.db.DbTestUtils; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; + public class Test2214To30DBUpgrade extends TestCase { private static final Logger s_logger = Logger .getLogger(Test2214To30DBUpgrade.class); + @Inject DatabaseUpgradeChecker checker; + @Override @Before public void setUp() throws Exception { @@ -56,9 +61,6 @@ public class Test2214To30DBUpgrade extends TestCase { "fake.sql", false, true); - DatabaseUpgradeChecker checker = ComponentLocator - .inject(DatabaseUpgradeChecker.class); - checker.upgrade("2.2.14", "3.0.0"); Connection conn = Transaction.getStandaloneConnection(); diff --git a/server/test/com/cloud/upgrade/Usage217To224UpgradeTest.java b/server/test/com/cloud/upgrade/Usage217To224UpgradeTest.java index 17a85fb6399..741af5a03f0 100644 --- a/server/test/com/cloud/upgrade/Usage217To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/Usage217To224UpgradeTest.java @@ -22,6 +22,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -36,6 +38,9 @@ import com.cloud.utils.db.Transaction; public class Usage217To224UpgradeTest extends TestCase { private static final Logger s_logger = Logger.getLogger(Usage217To224UpgradeTest.class); + @Inject VersionDaoImpl dao; + @Inject PremiumDatabaseUpgradeChecker checker; + @Override @Before public void setUp() throws Exception { @@ -56,8 +61,6 @@ public class Usage217To224UpgradeTest extends TestCase { Connection conn; PreparedStatement pstmt; - VersionDaoImpl dao = ComponentLocator.inject(VersionDaoImpl.class); - PremiumDatabaseUpgradeChecker checker = ComponentLocator.inject(PremiumDatabaseUpgradeChecker.class); String version = dao.getCurrentVersion(); assert version.equals("2.1.7") : "Version returned is not 2.1.7 but " + version; diff --git a/server/test/com/cloud/upgrade/UsageEvents218To224UpgradeTest.java b/server/test/com/cloud/upgrade/UsageEvents218To224UpgradeTest.java index 94ae83628da..cde114b5e63 100644 --- a/server/test/com/cloud/upgrade/UsageEvents218To224UpgradeTest.java +++ b/server/test/com/cloud/upgrade/UsageEvents218To224UpgradeTest.java @@ -22,6 +22,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -36,6 +38,9 @@ import com.cloud.utils.db.Transaction; public class UsageEvents218To224UpgradeTest extends TestCase { private static final Logger s_logger = Logger.getLogger(UsageEvents218To224UpgradeTest.class); + @Inject VersionDaoImpl dao; + @Inject DatabaseUpgradeChecker checker; + @Override @Before public void setUp() throws Exception { @@ -54,9 +59,6 @@ public class UsageEvents218To224UpgradeTest extends TestCase { Connection conn; PreparedStatement pstmt; - VersionDaoImpl dao = ComponentLocator.inject(VersionDaoImpl.class); - DatabaseUpgradeChecker checker = ComponentLocator.inject(DatabaseUpgradeChecker.class); - String version = dao.getCurrentVersion(); assert version.equals("2.1.8") : "Version returned is not 2.1.8 but " + version; diff --git a/server/test/com/cloud/vm/dao/UserVmDaoImplTest.java b/server/test/com/cloud/vm/dao/UserVmDaoImplTest.java index b062dd2a414..1a5a9008d92 100644 --- a/server/test/com/cloud/vm/dao/UserVmDaoImplTest.java +++ b/server/test/com/cloud/vm/dao/UserVmDaoImplTest.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.vm.dao; +import javax.inject.Inject; + import junit.framework.TestCase; import com.cloud.hypervisor.Hypervisor.HypervisorType; @@ -25,8 +27,9 @@ import com.cloud.vm.VirtualMachine; public class UserVmDaoImplTest extends TestCase { - public void testPersist() { - UserVmDao dao = ComponentLocator.inject(UserVmDaoImpl.class); + @Inject UserVmDao dao; + + public void testPersist() { dao.expunge(1000l); diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index 4403d75fbd0..000fea6b377 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -66,7 +66,6 @@ import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import com.cloud.user.Account; import com.cloud.user.User; import com.cloud.utils.Pair; -import com.cloud.utils.component.Adapters; import com.cloud.utils.component.Manager; import com.cloud.vm.*; import com.cloud.vpc.dao.MockVpcVirtualRouterElement; diff --git a/server/test/com/cloud/vpc/MockVpcManagerImpl.java b/server/test/com/cloud/vpc/MockVpcManagerImpl.java index c41bb7b27e8..e7d888e992f 100644 --- a/server/test/com/cloud/vpc/MockVpcManagerImpl.java +++ b/server/test/com/cloud/vpc/MockVpcManagerImpl.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.Set; import javax.ejb.Local; +import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.acl.ControlledEntity.ACLType; @@ -57,7 +58,7 @@ import com.cloud.vpc.dao.MockVpcDaoImpl; @Local(value = { VpcManager.class, VpcService.class }) public class MockVpcManagerImpl implements VpcManager, Manager{ - MockVpcDaoImpl _vpcDao = ComponentLocator.inject(MockVpcDaoImpl.class); + @Inject MockVpcDaoImpl _vpcDao; /* (non-Javadoc) * @see com.cloud.network.vpc.VpcService#getVpcOffering(long) diff --git a/server/test/com/cloud/vpc/VpcApiUnitTest.java b/server/test/com/cloud/vpc/VpcApiUnitTest.java index 14e376a9ee8..9693f443130 100644 --- a/server/test/com/cloud/vpc/VpcApiUnitTest.java +++ b/server/test/com/cloud/vpc/VpcApiUnitTest.java @@ -19,6 +19,8 @@ package com.cloud.vpc; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -46,7 +48,6 @@ import com.cloud.user.AccountVO; import com.cloud.user.MockAccountManagerImpl; import com.cloud.user.dao.AccountDaoImpl; -import com.cloud.utils.component.MockComponentLocator; import com.cloud.vm.dao.DomainRouterDaoImpl; import com.cloud.vpc.dao.MockNetworkDaoImpl; import com.cloud.vpc.dao.MockNetworkOfferingDaoImpl; @@ -58,45 +59,11 @@ import com.cloud.vpc.dao.MockVpcOfferingServiceMapDaoImpl; public class VpcApiUnitTest extends TestCase{ private static final Logger s_logger = Logger.getLogger(VpcApiUnitTest.class); - MockComponentLocator _locator; - VpcManager _vpcService; + @Inject VpcManager _vpcService; @Override @Before public void setUp() throws Exception { - _locator = new MockComponentLocator(ManagementService.Name); - _locator.addDao("VpcDao", MockVpcDaoImpl.class); - _locator.addDao("VpcOfferingDao", VpcOfferingDaoImpl.class); - _locator.addDao("ConfigurationDao", ConfigurationDaoImpl.class); - _locator.addDao("NetworkDao", MockNetworkDaoImpl.class); - _locator.addDao("IPAddressDao", IPAddressDaoImpl.class); - _locator.addDao("DomainRouterDao", DomainRouterDaoImpl.class); - _locator.addDao("VpcGatewayDao", VpcGatewayDaoImpl.class); - _locator.addDao("PrivateIpDao", PrivateIpDaoImpl.class); - _locator.addDao("StaticRouteDao", StaticRouteDaoImpl.class); - _locator.addDao("NetworkOfferingServiceMapDao", MockNetworkOfferingServiceMapDaoImpl.class); - _locator.addDao("VpcOfferingServiceMapDao", MockVpcOfferingServiceMapDaoImpl.class); - _locator.addDao("PhysicalNetworkDao", PhysicalNetworkDaoImpl.class); - _locator.addDao("ResourceTagDao", ResourceTagsDaoImpl.class); - _locator.addDao("FirewallRulesDao", FirewallRulesDaoImpl.class); - _locator.addDao("VlanDao", VlanDaoImpl.class); - _locator.addDao("AccountDao", AccountDaoImpl.class); - _locator.addDao("ResourceCountDao", ResourceCountDaoImpl.class); - _locator.addDao("NetworkOfferingDao", MockNetworkOfferingDaoImpl.class); - _locator.addDao("NetworkServiceMapDao", MockNetworkServiceMapDaoImpl.class); - _locator.addDao("VpcOfferingDao", MockVpcOfferingDaoImpl.class); - _locator.addDao("Site2SiteVpnDao", Site2SiteVpnGatewayDaoImpl.class); - - _locator.addManager("ConfigService", MockConfigurationManagerImpl.class); - _locator.addManager("vpc manager", VpcManagerImpl.class); - _locator.addManager("account manager", MockAccountManagerImpl.class); - _locator.addManager("network manager", MockNetworkManagerImpl.class); - _locator.addManager("Site2SiteVpnManager", MockSite2SiteVpnManagerImpl.class); - _locator.addManager("ResourceLimitService", MockResourceLimitManagerImpl.class); - - _locator.makeActive(null); - - _vpcService = ComponentLocator.inject(VpcManagerImpl.class); } public void test() { diff --git a/usage/src/com/cloud/usage/UsageManagerImpl.java b/usage/src/com/cloud/usage/UsageManagerImpl.java index 971f5dfa707..4944a14b14e 100644 --- a/usage/src/com/cloud/usage/UsageManagerImpl.java +++ b/usage/src/com/cloud/usage/UsageManagerImpl.java @@ -30,6 +30,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.ejb.Local; +import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; @@ -89,23 +90,23 @@ public class UsageManagerImpl implements UsageManager, Runnable { private static final int THREE_DAYS_IN_MINUTES = 60 * 24 * 3; private static final int USAGE_AGGREGATION_RANGE_MIN = 10; - private final ComponentLocator _locator = ComponentLocator.getLocator(UsageServer.Name, "usage-components.xml", "log4j-cloud_usage"); - private final AccountDao m_accountDao = _locator.getDao(AccountDao.class); - private final UserStatisticsDao m_userStatsDao = _locator.getDao(UserStatisticsDao.class); - private final UsageDao m_usageDao = _locator.getDao(UsageDao.class); - private final UsageVMInstanceDao m_usageInstanceDao = _locator.getDao(UsageVMInstanceDao.class); - private final UsageIPAddressDao m_usageIPAddressDao = _locator.getDao(UsageIPAddressDao.class); - private final UsageNetworkDao m_usageNetworkDao = _locator.getDao(UsageNetworkDao.class); - private final UsageVolumeDao m_usageVolumeDao = _locator.getDao(UsageVolumeDao.class); - private final UsageStorageDao m_usageStorageDao = _locator.getDao(UsageStorageDao.class); - private final UsageLoadBalancerPolicyDao m_usageLoadBalancerPolicyDao = _locator.getDao(UsageLoadBalancerPolicyDao.class); - private final UsagePortForwardingRuleDao m_usagePortForwardingRuleDao = _locator.getDao(UsagePortForwardingRuleDao.class); - private final UsageNetworkOfferingDao m_usageNetworkOfferingDao = _locator.getDao(UsageNetworkOfferingDao.class); - private final UsageVPNUserDao m_usageVPNUserDao = _locator.getDao(UsageVPNUserDao.class); - private final UsageSecurityGroupDao m_usageSecurityGroupDao = _locator.getDao(UsageSecurityGroupDao.class); - private final UsageJobDao m_usageJobDao = _locator.getDao(UsageJobDao.class); + @Inject private AccountDao m_accountDao; + @Inject private UserStatisticsDao m_userStatsDao; + @Inject private UsageDao m_usageDao; + @Inject private UsageVMInstanceDao m_usageInstanceDao; + @Inject private UsageIPAddressDao m_usageIPAddressDao; + @Inject private UsageNetworkDao m_usageNetworkDao; + @Inject private UsageVolumeDao m_usageVolumeDao; + @Inject private UsageStorageDao m_usageStorageDao; + @Inject private UsageLoadBalancerPolicyDao m_usageLoadBalancerPolicyDao; + @Inject private UsagePortForwardingRuleDao m_usagePortForwardingRuleDao; + @Inject private UsageNetworkOfferingDao m_usageNetworkOfferingDao; + @Inject private UsageVPNUserDao m_usageVPNUserDao; + @Inject private UsageSecurityGroupDao m_usageSecurityGroupDao; + @Inject private UsageJobDao m_usageJobDao; @Inject protected AlertManager _alertMgr; @Inject protected UsageEventDao _usageEventDao; + @Inject ConfigurationDao _configDao; private String m_version = null; private String m_name = null; @@ -152,14 +153,7 @@ public class UsageManagerImpl implements UsageManager, Runnable { m_name = name; - ComponentLocator locator = ComponentLocator.getCurrentLocator(); - ConfigurationDao configDao = locator.getDao(ConfigurationDao.class); - if (configDao == null) { - s_logger.error("Unable to get the configuration dao."); - return false; - } - - Map configs = configDao.getConfiguration(params); + Map configs = _configDao.getConfiguration(params); if (params != null) { mergeConfigs(configs, params); diff --git a/usage/src/com/cloud/usage/UsageServer.java b/usage/src/com/cloud/usage/UsageServer.java index 5fe46e68d6d..eaf91328929 100644 --- a/usage/src/com/cloud/usage/UsageServer.java +++ b/usage/src/com/cloud/usage/UsageServer.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.usage; +import javax.inject.Inject; + import org.apache.log4j.Logger; @@ -24,6 +26,7 @@ public class UsageServer { private static final Logger s_logger = Logger.getLogger(UsageServer.class.getName()); public static final String Name = "usage-server"; + @Inject UsageManager mgr; /** * @param args */ @@ -38,7 +41,6 @@ public class UsageServer { } public void start() { - UsageManager mgr = new UsageManager(); if (mgr != null) { if (s_logger.isInfoEnabled()) { s_logger.info("UsageServer ready..."); diff --git a/usage/src/com/cloud/usage/parser/IPAddressUsageParser.java b/usage/src/com/cloud/usage/parser/IPAddressUsageParser.java index f60c0bf6b40..a5a40c0fa04 100644 --- a/usage/src/com/cloud/usage/parser/IPAddressUsageParser.java +++ b/usage/src/com/cloud/usage/parser/IPAddressUsageParser.java @@ -22,7 +22,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.PostConstruct; +import javax.inject.Inject; + import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.usage.UsageIPAddressVO; import com.cloud.usage.UsageServer; @@ -33,15 +37,23 @@ import com.cloud.usage.dao.UsageIPAddressDao; import com.cloud.user.AccountVO; import com.cloud.utils.Pair; - +@Component public class IPAddressUsageParser { public static final Logger s_logger = Logger.getLogger(IPAddressUsageParser.class.getName()); - private static ComponentLocator _locator = ComponentLocator.getLocator(UsageServer.Name, "usage-components.xml", "log4j-cloud_usage"); - private static UsageDao m_usageDao = _locator.getDao(UsageDao.class); - private static UsageIPAddressDao m_usageIPAddressDao = _locator.getDao(UsageIPAddressDao.class); + private static UsageDao m_usageDao; + private static UsageIPAddressDao m_usageIPAddressDao; + + @Inject private UsageDao _usageDao; + @Inject private UsageIPAddressDao _usageIPAddressDao; + @PostConstruct + void init() { + m_usageDao = _usageDao; + m_usageIPAddressDao = _usageIPAddressDao; + } + public static boolean parse(AccountVO account, Date startDate, Date endDate) { if (s_logger.isDebugEnabled()) { s_logger.debug("Parsing IP Address usage for account: " + account.getId()); diff --git a/usage/src/com/cloud/usage/parser/LoadBalancerUsageParser.java b/usage/src/com/cloud/usage/parser/LoadBalancerUsageParser.java index 8763527b398..edea320aa08 100644 --- a/usage/src/com/cloud/usage/parser/LoadBalancerUsageParser.java +++ b/usage/src/com/cloud/usage/parser/LoadBalancerUsageParser.java @@ -22,7 +22,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.PostConstruct; +import javax.inject.Inject; + import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.usage.UsageLoadBalancerPolicyVO; import com.cloud.usage.UsageServer; @@ -33,13 +37,21 @@ import com.cloud.usage.dao.UsageLoadBalancerPolicyDao; import com.cloud.user.AccountVO; import com.cloud.utils.Pair; - +@Component public class LoadBalancerUsageParser { public static final Logger s_logger = Logger.getLogger(LoadBalancerUsageParser.class.getName()); - private static ComponentLocator _locator = ComponentLocator.getLocator(UsageServer.Name, "usage-components.xml", "log4j-cloud_usage"); - private static UsageDao m_usageDao = _locator.getDao(UsageDao.class); - private static UsageLoadBalancerPolicyDao m_usageLoadBalancerPolicyDao = _locator.getDao(UsageLoadBalancerPolicyDao.class); + private static UsageDao m_usageDao; + private static UsageLoadBalancerPolicyDao m_usageLoadBalancerPolicyDao; + + @Inject private UsageDao _usageDao; + @Inject private UsageLoadBalancerPolicyDao _usageLoadBalancerPolicyDao; + + @PostConstruct + void init() { + m_usageDao = _usageDao; + m_usageLoadBalancerPolicyDao = _usageLoadBalancerPolicyDao; + } public static boolean parse(AccountVO account, Date startDate, Date endDate) { if (s_logger.isDebugEnabled()) { diff --git a/usage/src/com/cloud/usage/parser/NetworkOfferingUsageParser.java b/usage/src/com/cloud/usage/parser/NetworkOfferingUsageParser.java index c2bf1c30427..f6ddf9f1bbb 100644 --- a/usage/src/com/cloud/usage/parser/NetworkOfferingUsageParser.java +++ b/usage/src/com/cloud/usage/parser/NetworkOfferingUsageParser.java @@ -22,6 +22,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.PostConstruct; +import javax.inject.Inject; + import org.apache.log4j.Logger; import com.cloud.usage.UsageNetworkOfferingVO; @@ -37,9 +40,17 @@ import com.cloud.utils.Pair; public class NetworkOfferingUsageParser { public static final Logger s_logger = Logger.getLogger(NetworkOfferingUsageParser.class.getName()); - private static ComponentLocator _locator = ComponentLocator.getLocator(UsageServer.Name, "usage-components.xml", "log4j-cloud_usage"); - private static UsageDao m_usageDao = _locator.getDao(UsageDao.class); - private static UsageNetworkOfferingDao m_usageNetworkOfferingDao = _locator.getDao(UsageNetworkOfferingDao.class); + private static UsageDao m_usageDao; + private static UsageNetworkOfferingDao m_usageNetworkOfferingDao; + + @Inject private UsageDao _usageDao; + @Inject private UsageNetworkOfferingDao _usageNetworkOfferingDao; + + @PostConstruct + void init() { + m_usageDao = _usageDao; + m_usageNetworkOfferingDao = _usageNetworkOfferingDao; + } public static boolean parse(AccountVO account, Date startDate, Date endDate) { if (s_logger.isDebugEnabled()) { diff --git a/usage/src/com/cloud/usage/parser/NetworkUsageParser.java b/usage/src/com/cloud/usage/parser/NetworkUsageParser.java index c0ba15f08c4..fb673d73c5f 100644 --- a/usage/src/com/cloud/usage/parser/NetworkUsageParser.java +++ b/usage/src/com/cloud/usage/parser/NetworkUsageParser.java @@ -21,26 +21,35 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.PostConstruct; +import javax.inject.Inject; + import org.apache.log4j.Logger; import com.cloud.usage.UsageNetworkVO; -import com.cloud.usage.UsageServer; import com.cloud.usage.UsageTypes; import com.cloud.usage.UsageVO; import com.cloud.usage.dao.UsageDao; import com.cloud.usage.dao.UsageNetworkDao; import com.cloud.user.AccountVO; -import com.cloud.utils.Pair; import com.cloud.utils.db.SearchCriteria; public class NetworkUsageParser { public static final Logger s_logger = Logger.getLogger(NetworkUsageParser.class.getName()); - private static ComponentLocator _locator = ComponentLocator.getLocator(UsageServer.Name, "usage-components.xml", "log4j-cloud_usage"); - private static UsageDao m_usageDao = _locator.getDao(UsageDao.class); - private static UsageNetworkDao m_usageNetworkDao = _locator.getDao(UsageNetworkDao.class); + private static UsageDao m_usageDao; + private static UsageNetworkDao m_usageNetworkDao; + @Inject private UsageDao _usageDao; + @Inject private UsageNetworkDao _usageNetworkDao; + + @PostConstruct + void init() { + m_usageDao = _usageDao; + m_usageNetworkDao = _usageNetworkDao; + } + public static boolean parse(AccountVO account, Date startDate, Date endDate) { if (s_logger.isDebugEnabled()) { s_logger.debug("Parsing all Network usage events for account: " + account.getId()); diff --git a/usage/src/com/cloud/usage/parser/PortForwardingUsageParser.java b/usage/src/com/cloud/usage/parser/PortForwardingUsageParser.java index 42cc74c10fb..16921804aa8 100644 --- a/usage/src/com/cloud/usage/parser/PortForwardingUsageParser.java +++ b/usage/src/com/cloud/usage/parser/PortForwardingUsageParser.java @@ -22,6 +22,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.PostConstruct; +import javax.inject.Inject; + import org.apache.log4j.Logger; import com.cloud.usage.UsagePortForwardingRuleVO; @@ -37,9 +40,17 @@ import com.cloud.utils.Pair; public class PortForwardingUsageParser { public static final Logger s_logger = Logger.getLogger(PortForwardingUsageParser.class.getName()); - private static ComponentLocator _locator = ComponentLocator.getLocator(UsageServer.Name, "usage-components.xml", "log4j-cloud_usage"); - private static UsageDao m_usageDao = _locator.getDao(UsageDao.class); - private static UsagePortForwardingRuleDao m_usagePFRuleDao = _locator.getDao(UsagePortForwardingRuleDao.class); + private static UsageDao m_usageDao; + private static UsagePortForwardingRuleDao m_usagePFRuleDao; + + @Inject private UsageDao _usageDao; + @Inject private UsagePortForwardingRuleDao _usagePFRuleDao; + + @PostConstruct + void init() { + m_usageDao = _usageDao; + _usagePFRuleDao = _usagePFRuleDao; + } public static boolean parse(AccountVO account, Date startDate, Date endDate) { if (s_logger.isDebugEnabled()) { diff --git a/usage/src/com/cloud/usage/parser/SecurityGroupUsageParser.java b/usage/src/com/cloud/usage/parser/SecurityGroupUsageParser.java index f175ae27d19..ed7acf348e1 100644 --- a/usage/src/com/cloud/usage/parser/SecurityGroupUsageParser.java +++ b/usage/src/com/cloud/usage/parser/SecurityGroupUsageParser.java @@ -22,6 +22,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.PostConstruct; +import javax.inject.Inject; + import org.apache.log4j.Logger; import com.cloud.usage.UsageSecurityGroupVO; @@ -37,9 +40,17 @@ import com.cloud.utils.Pair; public class SecurityGroupUsageParser { public static final Logger s_logger = Logger.getLogger(SecurityGroupUsageParser.class.getName()); - private static ComponentLocator _locator = ComponentLocator.getLocator(UsageServer.Name, "usage-components.xml", "log4j-cloud_usage"); - private static UsageDao m_usageDao = _locator.getDao(UsageDao.class); - private static UsageSecurityGroupDao m_usageSecurityGroupDao = _locator.getDao(UsageSecurityGroupDao.class); + private static UsageDao m_usageDao; + private static UsageSecurityGroupDao m_usageSecurityGroupDao; + + @Inject private UsageDao _usageDao; + @Inject private UsageSecurityGroupDao _usageSecurityGroupDao; + + @PostConstruct + void init() { + m_usageDao = _usageDao; + m_usageSecurityGroupDao = _usageSecurityGroupDao; + } public static boolean parse(AccountVO account, Date startDate, Date endDate) { if (s_logger.isDebugEnabled()) { diff --git a/usage/src/com/cloud/usage/parser/StorageUsageParser.java b/usage/src/com/cloud/usage/parser/StorageUsageParser.java index a0fee2df535..7542063fca3 100644 --- a/usage/src/com/cloud/usage/parser/StorageUsageParser.java +++ b/usage/src/com/cloud/usage/parser/StorageUsageParser.java @@ -22,6 +22,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.PostConstruct; +import javax.inject.Inject; + import org.apache.log4j.Logger; import com.cloud.usage.StorageTypes; @@ -38,9 +41,17 @@ import com.cloud.utils.Pair; public class StorageUsageParser { public static final Logger s_logger = Logger.getLogger(StorageUsageParser.class.getName()); - private static ComponentLocator _locator = ComponentLocator.getLocator(UsageServer.Name, "usage-components.xml", "log4j-cloud_usage"); - private static UsageDao m_usageDao = _locator.getDao(UsageDao.class); - private static UsageStorageDao m_usageStorageDao = _locator.getDao(UsageStorageDao.class); + private static UsageDao m_usageDao; + private static UsageStorageDao m_usageStorageDao; + + @Inject private UsageDao _usageDao; + @Inject private UsageStorageDao _usageStorageDao; + + @PostConstruct + void init() { + m_usageDao = _usageDao; + m_usageStorageDao = _usageStorageDao; + } public static boolean parse(AccountVO account, Date startDate, Date endDate) { if (s_logger.isDebugEnabled()) { diff --git a/usage/src/com/cloud/usage/parser/VMInstanceUsageParser.java b/usage/src/com/cloud/usage/parser/VMInstanceUsageParser.java index 0680ec3afb9..8d2e465aa89 100644 --- a/usage/src/com/cloud/usage/parser/VMInstanceUsageParser.java +++ b/usage/src/com/cloud/usage/parser/VMInstanceUsageParser.java @@ -22,6 +22,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.PostConstruct; +import javax.inject.Inject; + import org.apache.log4j.Logger; import com.cloud.usage.UsageServer; @@ -37,9 +40,17 @@ import com.cloud.utils.Pair; public class VMInstanceUsageParser { public static final Logger s_logger = Logger.getLogger(VMInstanceUsageParser.class.getName()); - private static ComponentLocator _locator = ComponentLocator.getLocator(UsageServer.Name, "usage-components.xml", "log4j-cloud_usage"); - private static UsageDao m_usageDao = _locator.getDao(UsageDao.class); - private static UsageVMInstanceDao m_usageInstanceDao = _locator.getDao(UsageVMInstanceDao.class); + private static UsageDao m_usageDao; + private static UsageVMInstanceDao m_usageInstanceDao; + + @Inject private static UsageDao _usageDao;; + @Inject private static UsageVMInstanceDao _usageInstanceDao; + + @PostConstruct + void init() { + m_usageDao = _usageDao; + m_usageInstanceDao = _usageInstanceDao; + } public static boolean parse(AccountVO account, Date startDate, Date endDate) { if (s_logger.isDebugEnabled()) { diff --git a/usage/src/com/cloud/usage/parser/VPNUserUsageParser.java b/usage/src/com/cloud/usage/parser/VPNUserUsageParser.java index 7a96e3ce7b0..c9a863b99d6 100644 --- a/usage/src/com/cloud/usage/parser/VPNUserUsageParser.java +++ b/usage/src/com/cloud/usage/parser/VPNUserUsageParser.java @@ -22,6 +22,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.PostConstruct; +import javax.inject.Inject; + import org.apache.log4j.Logger; import com.cloud.usage.UsageVPNUserVO; @@ -37,9 +40,17 @@ import com.cloud.utils.Pair; public class VPNUserUsageParser { public static final Logger s_logger = Logger.getLogger(VPNUserUsageParser.class.getName()); - private static ComponentLocator _locator = ComponentLocator.getLocator(UsageServer.Name, "usage-components.xml", "log4j-cloud_usage"); - private static UsageDao m_usageDao = _locator.getDao(UsageDao.class); - private static UsageVPNUserDao m_usageVPNUserDao = _locator.getDao(UsageVPNUserDao.class); + private static UsageDao m_usageDao; + private static UsageVPNUserDao m_usageVPNUserDao; + + @Inject private UsageDao _usageDao; + @Inject private UsageVPNUserDao _usageVPNUserDao; + + @PostConstruct + void init() { + m_usageDao = _usageDao; + m_usageVPNUserDao = _usageVPNUserDao; + } public static boolean parse(AccountVO account, Date startDate, Date endDate) { if (s_logger.isDebugEnabled()) { diff --git a/usage/src/com/cloud/usage/parser/VolumeUsageParser.java b/usage/src/com/cloud/usage/parser/VolumeUsageParser.java index 1eb7664c290..e797f1c5f2f 100644 --- a/usage/src/com/cloud/usage/parser/VolumeUsageParser.java +++ b/usage/src/com/cloud/usage/parser/VolumeUsageParser.java @@ -22,6 +22,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.PostConstruct; +import javax.inject.Inject; + import org.apache.log4j.Logger; import com.cloud.usage.UsageServer; @@ -37,9 +40,17 @@ import com.cloud.utils.Pair; public class VolumeUsageParser { public static final Logger s_logger = Logger.getLogger(VolumeUsageParser.class.getName()); - private static ComponentLocator _locator = ComponentLocator.getLocator(UsageServer.Name, "usage-components.xml", "log4j-cloud_usage"); - private static UsageDao m_usageDao = _locator.getDao(UsageDao.class); - private static UsageVolumeDao m_usageVolumeDao = _locator.getDao(UsageVolumeDao.class); + private static UsageDao m_usageDao; + private static UsageVolumeDao m_usageVolumeDao; + + @Inject private UsageDao _usageDao; + @Inject private UsageVolumeDao _usageVolumeDao; + + @PostConstruct + void init() { + m_usageDao = _usageDao; + m_usageVolumeDao = _usageVolumeDao; + } public static boolean parse(AccountVO account, Date startDate, Date endDate) { if (s_logger.isDebugEnabled()) { From 7960dd429b2d59382845f664a81c72e4024442b5 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 11 Jan 2013 14:03:05 -0800 Subject: [PATCH 53/73] utils: Fix getByUuid to accept string arg, it's not gonna be anything else Due to generic programming, most classes declare Daos with ID as Long, so they get the getUuid(Long) definition, it has to be getUuid(String), uuid is not gonna be anything else. Fix GenericDaoBase and GenericDao. Signed-off-by: Rohit Yadav --- utils/src/com/cloud/utils/db/GenericDao.java | 2 +- utils/src/com/cloud/utils/db/GenericDaoBase.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/src/com/cloud/utils/db/GenericDao.java b/utils/src/com/cloud/utils/db/GenericDao.java index 2fae1afe43d..15d04b76a1c 100755 --- a/utils/src/com/cloud/utils/db/GenericDao.java +++ b/utils/src/com/cloud/utils/db/GenericDao.java @@ -56,7 +56,7 @@ public interface GenericDao { T findById(ID id, boolean fresh); // Finds one unique VO using uuid - T findByUuid(ID uuid); + T findByUuid(String uuid); /** * @return VO object ready to be used for update. It won't have any fields filled in. diff --git a/utils/src/com/cloud/utils/db/GenericDaoBase.java b/utils/src/com/cloud/utils/db/GenericDaoBase.java index 92e9e1c4405..880e9de22a8 100755 --- a/utils/src/com/cloud/utils/db/GenericDaoBase.java +++ b/utils/src/com/cloud/utils/db/GenericDaoBase.java @@ -915,7 +915,7 @@ public abstract class GenericDaoBase implements Gene @Override @DB(txn=false) @SuppressWarnings("unchecked") - public T findByUuid(final ID uuid) { + public T findByUuid(final String uuid) { SearchCriteria sc = createSearchCriteria(); sc.addAnd("uuid", SearchCriteria.Op.EQ, uuid); return findOneBy(sc); From 31dd412626ee3ab0bbdf99d8417d806b6a156582 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 11 Jan 2013 14:05:41 -0800 Subject: [PATCH 54/73] DomainService: Fix getDomain by uuid string Signed-off-by: Rohit Yadav --- api/src/com/cloud/user/DomainService.java | 2 ++ server/src/com/cloud/user/DomainManagerImpl.java | 5 +++++ server/test/com/cloud/user/MockDomainManagerImpl.java | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/api/src/com/cloud/user/DomainService.java b/api/src/com/cloud/user/DomainService.java index 6fbe1b9a8db..cd20060b710 100644 --- a/api/src/com/cloud/user/DomainService.java +++ b/api/src/com/cloud/user/DomainService.java @@ -30,6 +30,8 @@ public interface DomainService { Domain getDomain(long id); + Domain getDomain(String uuid); + /** * Return whether a domain is a child domain of a given domain. * diff --git a/server/src/com/cloud/user/DomainManagerImpl.java b/server/src/com/cloud/user/DomainManagerImpl.java index 123b8951b2e..54ca2ac44bd 100644 --- a/server/src/com/cloud/user/DomainManagerImpl.java +++ b/server/src/com/cloud/user/DomainManagerImpl.java @@ -85,6 +85,11 @@ public class DomainManagerImpl implements DomainManager, DomainService, Manager return _domainDao.findById(domainId); } + @Override + public Domain getDomain(String domainUuid) { + return _domainDao.findByUuid(domainUuid); + } + @Override public String getName() { return _name; diff --git a/server/test/com/cloud/user/MockDomainManagerImpl.java b/server/test/com/cloud/user/MockDomainManagerImpl.java index 6dc4d075b9e..9f49535ce68 100644 --- a/server/test/com/cloud/user/MockDomainManagerImpl.java +++ b/server/test/com/cloud/user/MockDomainManagerImpl.java @@ -46,6 +46,12 @@ public class MockDomainManagerImpl implements Manager, DomainManager { return null; } + @Override + public Domain getDomain(String uuid) { + // TODO Auto-generated method stub + return null; + } + @Override public boolean isChildDomain(Long parentId, Long childId) { // TODO Auto-generated method stub From 24687973a8ea8e2f665945295a6b34a19d93b02b Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 11 Jan 2013 14:07:27 -0800 Subject: [PATCH 55/73] ApiServer: Get rid of IdentityDao, reuse domain manager to get domain id Signed-off-by: Rohit Yadav --- server/src/com/cloud/api/ApiServer.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 7663e8e724a..d34af67cff4 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -131,7 +131,6 @@ import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CSExceptionErrorCode; -import com.cloud.uuididentity.dao.IdentityDao; public class ApiServer implements HttpRequestHandler { private static final Logger s_logger = Logger.getLogger(ApiServer.class.getName()); @@ -680,20 +679,13 @@ public class ApiServer implements HttpRequestHandler { if (ex instanceof ServerApiException && ((ServerApiException) ex).getErrorCode() == BaseCmd.UNSUPPORTED_ACTION_ERROR) { throw (ServerApiException) ex; } - s_logger.error("unable to verifty request signature", ex); + s_logger.error("unable to verify request signature", ex); } return false; } - public Long fetchDomainId(String domainUUID){ - ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); - IdentityDao identityDao = locator.getDao(IdentityDao.class); - try{ - Long domainId = identityDao.getIdentityId("domain", domainUUID); - return domainId; - }catch(InvalidParameterValueException ex){ - return null; - } + public Long fetchDomainId(String domainUUID) { + return _domainMgr.getDomain(domainUUID).getId(); } public void loginUser(HttpSession session, String username, String password, Long domainId, String domainPath, String loginIpAddress ,Map requestParameters) throws CloudAuthenticationException { From 8cdb40a416dba0c8baac6980772c3208127811e7 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 11 Jan 2013 15:27:18 -0800 Subject: [PATCH 56/73] NetworkManagerImpl: Add method to getNetwork by uuid Signed-off-by: Rohit Yadav --- server/src/com/cloud/network/NetworkManagerImpl.java | 6 ++++++ server/test/com/cloud/network/MockNetworkManagerImpl.java | 6 ++++++ server/test/com/cloud/vpc/MockNetworkManagerImpl.java | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index c5003760b68..206392dce49 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -2441,6 +2441,12 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag return _networksDao.findById(id); } + @Override + @DB + public Network getNetwork(String uuid) { + return _networksDao.findByUuid(uuid); + } + @Override public List getRemoteAccessVpnElements() { List elements = new ArrayList(); diff --git a/server/test/com/cloud/network/MockNetworkManagerImpl.java b/server/test/com/cloud/network/MockNetworkManagerImpl.java index 26a6e60f714..874e01767a9 100755 --- a/server/test/com/cloud/network/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/network/MockNetworkManagerImpl.java @@ -112,6 +112,12 @@ public class MockNetworkManagerImpl implements NetworkManager, Manager, NetworkS return null; } + @Override + public Network getNetwork(String networkUuid) { + // TODO Auto-generated method stub + return null; + } + @Override public IpAddress getIp(long id) { // TODO Auto-generated method stub diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index 8cb9dd53948..bd8d8bc3a69 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -156,6 +156,12 @@ public class MockNetworkManagerImpl implements NetworkManager, Manager{ return null; } + @Override + public Network getNetwork(String networkUuid) { + // TODO Auto-generated method stub + return null; + } + /* (non-Javadoc) * @see com.cloud.network.NetworkService#getIp(long) */ From 35544f26e57ec6eaffbc6e87e3536fefbc0dbec0 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 11 Jan 2013 15:29:15 -0800 Subject: [PATCH 57/73] DeployVmCmd: Remove usage of IdentityDao, use Network to get by id or uuid Signed-off-by: Rohit Yadav --- .../api/command/user/vm/DeployVMCmd.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index 28bb80f72d3..e675c83dd6f 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -147,10 +147,9 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { private List securityGroupNameList; @ACL(checkKeyAccess=true) - @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, entityType={Network.class,IpAddress.class}, + @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, entityType={Network.class, IpAddress.class}, description = "ip to network mapping. Can't be specified with networkIds parameter." + - " Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].networkid=204 - requests to" + - " use ip 10.10.10.11 in network id=204") + " Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].networkid=uuid - requests to use ip 10.10.10.11 in network id=uuid") private Map ipToNetworkList; @Parameter(name=ApiConstants.IP_ADDRESS, type=CommandType.STRING, description="the ip address for default vm's network") @@ -284,7 +283,17 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { Iterator iter = ipsCollection.iterator(); while (iter.hasNext()) { HashMap ips = (HashMap) iter.next(); - Long networkId = Long.valueOf(_responseGenerator.getIdentiyId("networks", ips.get("networkid"))); + Long networkId; + Network network = _networkService.getNetwork(ips.get("networkid")); + if (network != null) { + networkId = network.getId(); + } else { + try { + networkId = Long.parseLong(ips.get("networkid")); + } catch(NumberFormatException e) { + throw new InvalidParameterValueException("Unable to translate and find entity with networkId: " + ips.get("networkid")); + } + } String requestedIp = (String) ips.get("ip"); ipToNetworkMap.put(networkId, requestedIp); } From a35db97355470a30e2ea0979560a74afef26291e Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 11 Jan 2013 15:29:51 -0800 Subject: [PATCH 58/73] NetworkService: Add method to interface to get network by uuid Signed-off-by: Rohit Yadav --- api/src/com/cloud/network/NetworkService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/src/com/cloud/network/NetworkService.java b/api/src/com/cloud/network/NetworkService.java index d5841a4692e..39a746e6776 100755 --- a/api/src/com/cloud/network/NetworkService.java +++ b/api/src/com/cloud/network/NetworkService.java @@ -60,6 +60,8 @@ public interface NetworkService { Network getNetwork(long networkId); + Network getNetwork(String networkUuid); + IpAddress getIp(long id); NetworkProfile convertNetworkToNetworkProfile(long networkId); From 8eba0ee0bbb2dbdb97aee8e672405b5e174b510d Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 11 Jan 2013 15:31:03 -0800 Subject: [PATCH 59/73] ApiDispatcher: Remove helper method that proxies via ApiResponseHelper to get entity by IdentityDao Signed-off-by: Rohit Yadav --- server/src/com/cloud/api/ApiDispatcher.java | 6 ------ server/src/com/cloud/api/ApiResponseHelper.java | 5 ----- 2 files changed, 11 deletions(-) diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java index 7bc3271523b..55d7f429ca1 100755 --- a/server/src/com/cloud/api/ApiDispatcher.java +++ b/server/src/com/cloud/api/ApiDispatcher.java @@ -64,7 +64,6 @@ import com.cloud.utils.component.PluggableService; import com.cloud.utils.db.GenericDao; import com.cloud.utils.exception.CSExceptionErrorCode; import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.uuididentity.dao.IdentityDao; // ApiDispatcher: A class that dispatches API commands to the appropriate manager for execution. public class ApiDispatcher { @@ -75,7 +74,6 @@ public class ApiDispatcher { @Inject private AsyncJobManager _asyncMgr = null; @Inject private AccountManager _accountMgr = null; @Inject EntityManager _entityMgr = null; - @Inject IdentityDao _identityDao = null; Map> _daoNameMap = new HashMap>(); // singleton class @@ -708,8 +706,4 @@ public class ApiDispatcher { throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]"); } } - - public static Long getIdentiyId(String tableName, String token) { - return s_instance._identityDao.getIdentityId(tableName, token); - } } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index edb798b812e..c346a6b86f1 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -2791,11 +2791,6 @@ public class ApiResponseHelper implements ResponseGenerator { return response; } - @Override - public Long getIdentiyId(String tableName, String token) { - return ApiDispatcher.getIdentiyId(tableName, token); - } - @Override public ResourceTagResponse createResourceTagResponse(ResourceTag resourceTag, boolean keyValueOnly) { ResourceTagJoinVO rto = ApiDBUtils.newResourceTagView(resourceTag); From 83e7214b02adf1facc8aed0f61206248de698c42 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 11 Jan 2013 16:45:35 -0800 Subject: [PATCH 60/73] ConsoleProxyManagerImpl: Get rid of IdentityDao Signed-off-by: Rohit Yadav --- .../consoleproxy/ConsoleProxyManagerImpl.java | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index 4994f4f526b..5bc5d27ee08 100755 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -31,6 +31,8 @@ import javax.ejb.Local; import javax.naming.ConfigurationException; import javax.persistence.Table; +import com.cloud.offering.DiskOffering; +import com.cloud.storage.dao.DiskOfferingDao; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -107,7 +109,6 @@ import com.cloud.resource.UnableDeleteHostException; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.servlet.ConsoleProxyServlet; -import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StoragePoolVO; @@ -138,7 +139,6 @@ import com.cloud.utils.db.Transaction; import com.cloud.utils.events.SubscriptionMgr; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; -import com.cloud.uuididentity.dao.IdentityDao; import com.cloud.vm.ConsoleProxyVO; import com.cloud.vm.NicProfile; import com.cloud.vm.ReservationContext; @@ -216,6 +216,8 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx @Inject ServiceOfferingDao _offeringDao; @Inject + DiskOfferingDao _diskOfferingDao; + @Inject NetworkOfferingDao _networkOfferingDao; @Inject StoragePoolDao _storagePoolDao; @@ -224,8 +226,6 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx @Inject ResourceManager _resourceMgr; @Inject - IdentityDao _identityDao; - @Inject NetworkDao _networkDao; @Inject RulesManager _rulesMgr; @@ -928,14 +928,12 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx return new ConsoleAccessAuthenticationAnswer(cmd, false); } - vmId = _identityDao.getIdentityId("vm_instance", cmd.getVmId()); - if (vmId == null) { - s_logger.error("Invalid vm id " + cmd.getVmId() + " sent from console access authentication"); - return new ConsoleAccessAuthenticationAnswer(cmd, false); - } - - VMInstanceVO vm = _instanceDao.findById(vmId); + VirtualMachine vm = _instanceDao.findByUuid(cmd.getVmId()); if (vm == null) { + vm = _instanceDao.findById(Long.parseLong(cmd.getVmId())); + } + if (vm == null) { + s_logger.error("Invalid vm id " + cmd.getVmId() + " sent from console access authentication"); return new ConsoleAccessAuthenticationAnswer(cmd, false); } @@ -1525,16 +1523,13 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, ConsoleProx //check if there is a default service offering configured String cpvmSrvcOffIdStr = configs.get(Config.ConsoleProxyServiceOffering.key()); if (cpvmSrvcOffIdStr != null) { - - Long cpvmSrvcOffId = null; - try { - cpvmSrvcOffId = _identityDao.getIdentityId(DiskOfferingVO.class.getAnnotation(Table.class).name(),cpvmSrvcOffIdStr); - } catch (Exception e) { - String msg = "Can't find system service offering specified by global config, uuid=" + cpvmSrvcOffIdStr + " for console proxy vm"; - s_logger.warn(msg); - } - if(cpvmSrvcOffId != null){ - _serviceOffering = _offeringDao.findById(cpvmSrvcOffId); + DiskOffering diskOffering = _diskOfferingDao.findByUuid(cpvmSrvcOffIdStr); + if (diskOffering == null) + diskOffering = _diskOfferingDao.findById(Long.parseLong(cpvmSrvcOffIdStr)); + if (diskOffering != null) { + _serviceOffering = _offeringDao.findById(diskOffering.getId()); + } else { + s_logger.warn("Can't find system service offering specified by global config, uuid=" + cpvmSrvcOffIdStr + " for console proxy vm"); } } From 4c80684b1fffb14cea8ffd3880b278bd51dff9fc Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 11 Jan 2013 16:47:02 -0800 Subject: [PATCH 61/73] StaticRoleBasedAPIAccessChecker: Fix acl cfg processing error messages Signed-off-by: Rohit Yadav --- .../acl/StaticRoleBasedAPIAccessChecker.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) 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 740fbbc6456..380b6714517 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 @@ -43,9 +43,8 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIC protected StaticRoleBasedAPIAccessChecker() { super(); - for (RoleType roleType: RoleType.values()) { + for (RoleType roleType: RoleType.values()) s_roleBasedApisMap.put(roleType, new HashSet()); - } } @Override @@ -71,16 +70,14 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIC List services = locator.getAllPluggableServices(); services.add((PluggableService) ComponentLocator.getComponent(ManagementServer.Name)); - Map configPropertiesMap = new HashMap(); for (PluggableService service : services) { - configPropertiesMap.putAll(service.getProperties()); + processConfigFiles(service.getProperties(), service.getClass().toString()); + s_logger.info("Processed role based acl for: " + service.toString()); } - - processConfigFiles(configPropertiesMap); return true; } - private void processConfigFiles(Map configMap) { + private void processConfigFiles(Map configMap, String service) { for (Map.Entry entry: configMap.entrySet()) { String apiName = entry.getKey(); String roleMask = entry.getValue(); @@ -91,7 +88,8 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIC s_roleBasedApisMap.get(roleType).add(apiName); } } catch (NumberFormatException nfe) { - s_logger.info("Malformed commands.properties permissions value, for entry: " + entry.toString()); + s_logger.info("Malformed getProperties() value for service: " + service + + " for entry: " + entry.toString()); } } } From 40779975d33c554ce488a21d84e63e635e95984e Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 11 Jan 2013 18:39:57 -0800 Subject: [PATCH 62/73] ExtractVolumeCmd: Fix regression, use Zone Id for getting zone Fixes regression introduced in b14b39a69fa295d1d20484170b95b97fe310af28 Signed-off-by: Rohit Yadav --- .../cloudstack/api/command/user/volume/ExtractVolumeCmd.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java index 7f6cd052470..43b25a83663 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java @@ -137,7 +137,7 @@ public class ExtractVolumeCmd extends BaseAsyncCmd { Volume vol = _entityMgr.findById(Volume.class, id); response.setId(vol.getUuid()); response.setName(vol.getName()); - DataCenter zone = _entityMgr.findById(DataCenter.class, id); + DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId); response.setZoneId(zone.getUuid()); response.setZoneName(zone.getName()); response.setMode(mode); From 0dca44efe8f3adee3f18127791edac7b0e5c1f81 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 11 Jan 2013 18:43:26 -0800 Subject: [PATCH 63/73] ApiServer: Debug messages, don't spam with info, remove identity helper in ResponseGenerator Signed-off-by: Rohit Yadav --- .../apache/cloudstack/api/ResponseGenerator.java | 7 ------- server/src/com/cloud/api/ApiServer.java | 13 ++++++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/org/apache/cloudstack/api/ResponseGenerator.java index e9f988ade60..63df4dc5532 100644 --- a/api/src/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/org/apache/cloudstack/api/ResponseGenerator.java @@ -314,13 +314,6 @@ public interface ResponseGenerator { StorageNetworkIpRangeResponse createStorageNetworkIpRangeResponse(StorageNetworkIpRange result); - /** - * @param tableName TODO - * @param token - * @return - */ - Long getIdentiyId(String tableName, String token); - /** * @param resourceTag * @param keyValueOnly TODO diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index d34af67cff4..52f2aef56cb 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -601,30 +601,29 @@ public class ApiServer implements HttpRequestHandler { // if api/secret key are passed to the parameters if ((signature == null) || (apiKey == null)) { - if (s_logger.isDebugEnabled()) { - s_logger.info("expired session, missing signature, or missing apiKey -- ignoring request...sig: " + signature + ", apiKey: " + apiKey); - } + s_logger.debug("Expired session, missing signature, or missing apiKey -- ignoring request. Signature: " + signature + ", apiKey: " + apiKey); return false; // no signature, bad request } Date expiresTS = null; + // FIXME: Hard coded signature, why not have an enum if ("3".equals(signatureVersion)) { // New signature authentication. Check for expire parameter and its validity if (expires == null) { - s_logger.info("missing Expires parameter -- ignoring request...sig: " + signature + ", apiKey: " + apiKey); + s_logger.debug("Missing Expires parameter -- ignoring request. Signature: " + signature + ", apiKey: " + apiKey); return false; } synchronized (_dateFormat) { try { expiresTS = _dateFormat.parse(expires); } catch (ParseException pe) { - s_logger.info("Incorrect date format for Expires parameter", pe); + s_logger.debug("Incorrect date format for Expires parameter", pe); return false; } } Date now = new Date(System.currentTimeMillis()); if (expiresTS.before(now)) { - s_logger.info("Request expired -- ignoring ...sig: " + signature + ", apiKey: " + apiKey); + s_logger.debug("Request expired -- ignoring ...sig: " + signature + ", apiKey: " + apiKey); return false; } } @@ -635,7 +634,7 @@ public class ApiServer implements HttpRequestHandler { // verify there is a user with this api key Pair userAcctPair = _accountMgr.findUserByApiKey(apiKey); if (userAcctPair == null) { - s_logger.info("apiKey does not map to a valid user -- ignoring request, apiKey: " + apiKey); + s_logger.debug("apiKey does not map to a valid user -- ignoring request, apiKey: " + apiKey); return false; } From ad063ed61055ca26b23594b4c47e30a3c22974d7 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 11 Jan 2013 19:23:32 -0800 Subject: [PATCH 64/73] StaticRoleBasedAPIAccessChecker: Throw exception on failed check Plugin should not be responsible for existence of checking an API, this was wrong. Throw exception boldly when checkAccess fails. Signed-off-by: Rohit Yadav --- .../org/apache/cloudstack/acl/APIChecker.java | 5 ++--- .../acl/StaticRoleBasedAPIAccessChecker.java | 17 +++++++---------- server/src/com/cloud/api/ApiServer.java | 15 +++------------ 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/api/src/org/apache/cloudstack/acl/APIChecker.java b/api/src/org/apache/cloudstack/acl/APIChecker.java index 61dd7de75cb..b14dfe101ba 100644 --- a/api/src/org/apache/cloudstack/acl/APIChecker.java +++ b/api/src/org/apache/cloudstack/acl/APIChecker.java @@ -16,13 +16,12 @@ // under the License. package org.apache.cloudstack.acl; +import com.cloud.exception.PermissionDeniedException; import org.apache.cloudstack.acl.RoleType; import com.cloud.utils.component.Adapter; // 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); + boolean checkAccess(RoleType roleType, String apiCommandName) throws PermissionDeniedException; } 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 380b6714517..affd69ed89c 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,6 +16,7 @@ // under the License. package org.apache.cloudstack.acl; +import com.cloud.exception.PermissionDeniedException; import com.cloud.server.ManagementServer; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.component.ComponentLocator; @@ -48,17 +49,13 @@ public class StaticRoleBasedAPIAccessChecker extends AdapterBase implements APIC } @Override - public boolean checkAccess(RoleType roleType, String commandName) { - return s_roleBasedApisMap.get(roleType).contains(commandName); - } - - @Override - public boolean checkExistence(String apiName) { - for (RoleType roleType: RoleType.values()) { - if (s_roleBasedApisMap.get(roleType).contains(apiName)) - return true; + public boolean checkAccess(RoleType roleType, String commandName) + throws PermissionDeniedException { + boolean isAllowed = s_roleBasedApisMap.get(roleType).contains(commandName); + if (!isAllowed) { + throw new PermissionDeniedException("The API does not exist or is blacklisted. Role type=" + roleType.toString() + " is not allowed to request the api: " + commandName); } - return false; + return isAllowed; } @Override diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 52f2aef56cb..03462e488ef 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -556,7 +556,7 @@ public class ApiServer implements HttpRequestHandler { return true; } else { // check against every available command to see if the command exists or not - if (!doesCommandExist(commandName) && !commandName.equals("login") && !commandName.equals("logout")) { + if (!_apiNameCmdClassMap.containsKey(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"); } @@ -780,18 +780,9 @@ 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) { + private boolean isCommandAvailable(User user, String commandName) throws PermissionDeniedException { if (user == null) { - return false; + throw new PermissionDeniedException("User is null for role based API access check for command" + commandName); } Account account = _accountMgr.getAccount(user.getAccountId()); From 452e9c3efdef5c2b28b15f225f7022c74d1c24ed Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Sat, 12 Jan 2013 03:41:27 -0800 Subject: [PATCH 65/73] maven: Remove duplicate cloud-core dependency in server Signed-off-by: Rohit Yadav --- server/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/server/pom.xml b/server/pom.xml index 77f1fb816cd..8592f27e4b7 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -95,11 +95,6 @@ cloud-api ${project.version} - - org.apache.cloudstack - cloud-core - ${project.version} - install From fdc9103fbee6d8150d7933e216e3d7330032dcce Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Sat, 12 Jan 2013 03:43:35 -0800 Subject: [PATCH 66/73] create-schema: Fix deploydb, drop existing (new) tables Signed-off-by: Rohit Yadav --- setup/db/create-schema.sql | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index 990e7d8ca4f..864fe52224a 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -157,6 +157,43 @@ DROP TABLE IF EXISTS `cloud`.`autoscale_policies`; DROP TABLE IF EXISTS `cloud`.`counter`; DROP TABLE IF EXISTS `cloud`.`conditions`; DROP TABLE IF EXISTS `cloud`.`inline_load_balancer_nic_map`; +DROP TABLE IF EXISTS `cloud`.`cmd_exec_log`; +DROP TABLE IF EXISTS `cloud`.`keystore`; +DROP TABLE IF EXISTS `cloud`.`swift`; +DROP TABLE IF EXISTS `cloud`.`project_account`; +DROP TABLE IF EXISTS `cloud`.`project_invitations`; +DROP TABLE IF EXISTS `cloud`.`elastic_lb_vm_map`; +DROP TABLE IF EXISTS `cloud`.`ntwk_offering_service_map`; +DROP TABLE IF EXISTS `cloud`.`ntwk_service_map`; +DROP TABLE IF EXISTS `cloud`.`external_load_balancer_devices`; +DROP TABLE IF EXISTS `cloud`.`external_firewall_devices`; +DROP TABLE IF EXISTS `cloud`.`network_external_lb_device_map`; +DROP TABLE IF EXISTS `cloud`.`network_external_firewall_device_map`; +DROP TABLE IF EXISTS `cloud`.`virtual_router_providers`; +DROP TABLE IF EXISTS `cloud`.`op_user_stats_log`; +DROP TABLE IF EXISTS `cloud`.`netscaler_pod_ref`; +DROP TABLE IF EXISTS `cloud`.`mshost_peer`; +DROP TABLE IF EXISTS `cloud`.`vm_template_details`; +DROP TABLE IF EXISTS `cloud`.`hypervisor_capabilities`; +DROP TABLE IF EXISTS `cloud`.`template_swift_ref`; +DROP TABLE IF EXISTS `cloud`.`account_details`; +DROP TABLE IF EXISTS `cloud`.`vpc`; +DROP TABLE IF EXISTS `cloud`.`vpc_offerings`; +DROP TABLE IF EXISTS `cloud`.`vpc_offering_service_map`; +DROP TABLE IF EXISTS `cloud`.`vpc_gateways`; +DROP TABLE IF EXISTS `cloud`.`router_network_ref`; +DROP TABLE IF EXISTS `cloud`.`private_ip_address`; +DROP TABLE IF EXISTS `cloud`.`static_routes`; +DROP TABLE IF EXISTS `cloud`.`resource_tags`; +DROP TABLE IF EXISTS `cloud`.`primary_data_store_provider`; +DROP TABLE IF EXISTS `cloud`.`image_data_store_provider`; +DROP TABLE IF EXISTS `cloud`.`image_data_store`; +DROP TABLE IF EXISTS `cloud`.`vm_compute_tags`; +DROP TABLE IF EXISTS `cloud`.`vm_root_disk_tags`; +DROP TABLE IF EXISTS `cloud`.`vm_network_map`; +DROP TABLE IF EXISTS `cloud`.`netapp_volume`; +DROP TABLE IF EXISTS `cloud`.`netapp_pool`; +DROP TABLE IF EXISTS `cloud`.`netapp_lun`; CREATE TABLE `cloud`.`version` ( `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id', From ba20e7f85a971bfbbb90fe0c0817dd980bb2455c Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Sat, 12 Jan 2013 06:27:09 -0800 Subject: [PATCH 67/73] Fix license headers for java files in javelin Signed-off-by: Rohit Yadav --- .../engine/datacenter/entity/api/HostEntity.java | 16 ++++++++++++++++ .../api/storage/PrimaryDataStoreProvider.java | 16 ++++++++++++++++ .../subsystem/api/storage/StorageSubSystem.java | 16 ++++++++++++++++ .../subsystem/api/storage/disktype/QCOW2.java | 16 ++++++++++++++++ .../subsystem/api/storage/disktype/Unknown.java | 16 ++++++++++++++++ .../subsystem/api/storage/disktype/VHD.java | 16 ++++++++++++++++ .../subsystem/api/storage/disktype/VMDK.java | 16 ++++++++++++++++ .../api/storage/disktype/VolumeDiskType.java | 16 ++++++++++++++++ .../api/storage/disktype/VolumeDiskTypeBase.java | 16 ++++++++++++++++ .../storage/disktype/VolumeDiskTypeHelper.java | 16 ++++++++++++++++ .../subsystem/api/storage/type/BaseImage.java | 16 ++++++++++++++++ .../subsystem/api/storage/type/DataDisk.java | 16 ++++++++++++++++ .../engine/subsystem/api/storage/type/Iso.java | 16 ++++++++++++++++ .../subsystem/api/storage/type/RootDisk.java | 16 ++++++++++++++++ .../subsystem/api/storage/type/Unknown.java | 16 ++++++++++++++++ .../api/storage/type/VolumeTypeBase.java | 16 ++++++++++++++++ .../api/storage/type/VolumeTypeHelper.java | 16 ++++++++++++++++ .../engine/cloud/entity/api/VMEntityManager.java | 16 ++++++++++++++++ .../cloud/entity/api/VMEntityManagerImpl.java | 16 ++++++++++++++++ .../entity/api/VirtualMachineEntityImpl.java | 16 ++++++++++++++++ .../datacenter/entity/api/ClusterEntityImpl.java | 16 ++++++++++++++++ .../entity/api/DataCenterResourceManager.java | 16 ++++++++++++++++ .../api/DataCenterResourceManagerImpl.java | 16 ++++++++++++++++ .../datacenter/entity/api/HostEntityImpl.java | 16 ++++++++++++++++ .../test/ChildTestConfiguration.java | 16 ++++++++++++++++ .../provisioning/test/ProvisioningTest.java | 16 ++++++++++++++++ .../DefaultImageDataStoreLifeCycle.java | 16 ++++++++++++++++ .../apache/cloudstack/storage/test/AopTest.java | 16 ++++++++++++++++ .../cloudstack/storage/test/AopTestAdvice.java | 16 ++++++++++++++++ .../storage/test/ChildTestConfiguration.java | 16 ++++++++++++++++ .../storage/test/CloudStackTestNGBase.java | 16 ++++++++++++++++ .../cloudstack/storage/test/MockRpcCallBack.java | 16 ++++++++++++++++ .../storage/test/StorageFactoryBean.java | 16 ++++++++++++++++ .../cloudstack/storage/test/StorageTest.java | 16 ++++++++++++++++ .../storage/test/TestConfiguration.java | 16 ++++++++++++++++ .../apache/cloudstack/storage/test/TestNG.java | 16 ++++++++++++++++ .../cloudstack/storage/test/TestNGAop.java | 16 ++++++++++++++++ .../cloudstack/storage/test/XenEndpoint.java | 16 ++++++++++++++++ .../storage/snapshot/SnapshotServiceImpl.java | 16 ++++++++++++++++ .../strategy/HypervisorBasedSnapshot.java | 16 ++++++++++++++++ .../snapshot/strategy/StorageBasedSnapshot.java | 16 ++++++++++++++++ .../org/apache/cloudstack/storage/EndPoint.java | 16 ++++++++++++++++ .../backup/SnapshotOnBackupStoreInfo.java | 16 ++++++++++++++++ .../backup/datastore/BackupStoreInfo.java | 16 ++++++++++++++++ .../cloudstack/storage/command/CopyCmd.java | 16 ++++++++++++++++ .../CopyTemplateToPrimaryStorageAnswer.java | 16 ++++++++++++++++ .../command/CreatePrimaryDataStoreCmd.java | 16 ++++++++++++++++ .../cloudstack/storage/datastore/DataStore.java | 16 ++++++++++++++++ .../datastore/ObjectInDataStoreManager.java | 16 ++++++++++++++++ .../datastore/ObjectInDataStoreManagerImpl.java | 16 ++++++++++++++++ .../storage/datastore/TemplateInDataStore.java | 16 ++++++++++++++++ .../datastore/protocol/DataStoreProtocol.java | 16 ++++++++++++++++ .../storage/db/ObjectInDataStoreDao.java | 16 ++++++++++++++++ .../storage/db/ObjectInDataStoreDaoImpl.java | 16 ++++++++++++++++ .../storage/db/ObjectInDataStoreVO.java | 16 ++++++++++++++++ .../storage/snapshot/SnapshotEntityImpl.java | 16 ++++++++++++++++ .../storage/snapshot/SnapshotInfo.java | 16 ++++++++++++++++ .../storage/snapshot/SnapshotService.java | 16 ++++++++++++++++ .../storage/snapshot/SnapshotStrategy.java | 16 ++++++++++++++++ .../cloudstack/storage/to/ImageDataStoreTO.java | 16 ++++++++++++++++ .../storage/to/NfsPrimaryDataStoreTO.java | 16 ++++++++++++++++ .../storage/to/PrimaryDataStoreTO.java | 16 ++++++++++++++++ .../apache/cloudstack/storage/to/TemplateTO.java | 16 ++++++++++++++++ .../apache/cloudstack/storage/to/VolumeTO.java | 16 ++++++++++++++++ .../datastore/DefaultPrimaryDataStore.java | 16 ++++++++++++++++ .../AbstractPrimaryDataStoreConfigurator.java | 16 ++++++++++++++++ .../xen/AbstractXenConfigurator.java | 16 ++++++++++++++++ .../DefaultPrimaryDataStoreDriverImpl.java | 16 ++++++++++++++++ .../datastore/driver/PrimaryDataStoreDriver.java | 16 ++++++++++++++++ .../DefaultPrimaryDatastoreProviderImpl.java | 16 ++++++++++++++++ .../PrimaryDataStoreProviderManager.java | 16 ++++++++++++++++ .../cloudstack/storage/volume/VolumeObject.java | 16 ++++++++++++++++ .../ws/jackson/CSJacksonAnnotationTest.java | 16 ++++++++++++++++ .../driver/SolidfirePrimaryDataStoreDriver.java | 16 ++++++++++++++++ .../SolidfirePrimaryDataStoreProvider.java | 16 ++++++++++++++++ .../cloudstack/storage/test/AopTestAdvice.java | 16 ++++++++++++++++ .../storage/test/ChildTestConfiguration.java | 16 ++++++++++++++++ .../storage/test/TestConfiguration.java | 16 ++++++++++++++++ .../cloudstack/storage/test/VolumeTest.java | 16 ++++++++++++++++ 79 files changed, 1264 insertions(+) mode change 100755 => 100644 engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageSubSystem.java diff --git a/engine/api/src/org/apache/cloudstack/engine/datacenter/entity/api/HostEntity.java b/engine/api/src/org/apache/cloudstack/engine/datacenter/entity/api/HostEntity.java index 9da196ecd9a..99f3120f93a 100644 --- a/engine/api/src/org/apache/cloudstack/engine/datacenter/entity/api/HostEntity.java +++ b/engine/api/src/org/apache/cloudstack/engine/datacenter/entity/api/HostEntity.java @@ -1,3 +1,19 @@ +// 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.engine.datacenter.entity.api; import com.cloud.hypervisor.Hypervisor.HypervisorType; diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreProvider.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreProvider.java index c83594e6176..9aafebf41e4 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreProvider.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreProvider.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage; import java.util.Map; diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageSubSystem.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageSubSystem.java old mode 100755 new mode 100644 index eb78376305e..8043487d46b --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageSubSystem.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageSubSystem.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage; import java.net.URI; diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/QCOW2.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/QCOW2.java index 4daa4a714f1..b67a735d8f7 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/QCOW2.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/QCOW2.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.disktype; import org.springframework.stereotype.Component; diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/Unknown.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/Unknown.java index 5b52d5d8a72..6600405922b 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/Unknown.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/Unknown.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.disktype; public class Unknown extends VolumeDiskTypeBase { diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VHD.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VHD.java index a19dcaf5bf0..4f40e3e9bf8 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VHD.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VHD.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.disktype; import org.springframework.stereotype.Component; diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VMDK.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VMDK.java index bb06318487e..da0adfb33c0 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VMDK.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VMDK.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.disktype; import org.springframework.stereotype.Component; diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VolumeDiskType.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VolumeDiskType.java index 3e1462135bd..e670d6387c8 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VolumeDiskType.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VolumeDiskType.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.disktype; public interface VolumeDiskType { diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VolumeDiskTypeBase.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VolumeDiskTypeBase.java index 66c33a8e0f9..75f78031d52 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VolumeDiskTypeBase.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VolumeDiskTypeBase.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.disktype; public class VolumeDiskTypeBase implements VolumeDiskType { diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VolumeDiskTypeHelper.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VolumeDiskTypeHelper.java index ca65cd6a632..a2b5ede3c2b 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VolumeDiskTypeHelper.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/disktype/VolumeDiskTypeHelper.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.disktype; import java.util.List; diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/BaseImage.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/BaseImage.java index 633a6d54cf1..9991cedfa27 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/BaseImage.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/BaseImage.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.type; public class BaseImage extends VolumeTypeBase { diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/DataDisk.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/DataDisk.java index 11b40ce361b..762233e940f 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/DataDisk.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/DataDisk.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.type; import org.springframework.stereotype.Component; diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/Iso.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/Iso.java index 22274d5f744..43611b461b1 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/Iso.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/Iso.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.type; import org.springframework.stereotype.Component; diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/RootDisk.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/RootDisk.java index 96da41684a5..723d59c66cf 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/RootDisk.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/RootDisk.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.type; import org.springframework.stereotype.Component; diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/Unknown.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/Unknown.java index ba9a6ce88f6..6f8904a5af2 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/Unknown.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/Unknown.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.type; public class Unknown extends VolumeTypeBase { diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/VolumeTypeBase.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/VolumeTypeBase.java index c82961d2928..6ffd9d7c9c8 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/VolumeTypeBase.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/VolumeTypeBase.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.type; public class VolumeTypeBase implements VolumeType { diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/VolumeTypeHelper.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/VolumeTypeHelper.java index a02f524a730..f29dd08721f 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/VolumeTypeHelper.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/type/VolumeTypeHelper.java @@ -1,3 +1,19 @@ +// 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.engine.subsystem.api.storage.type; import java.util.List; diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManager.java b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManager.java index 06496454f94..37545e8f469 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManager.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManager.java @@ -1,3 +1,19 @@ +// 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.engine.cloud.entity.api; import org.apache.cloudstack.engine.cloud.entity.api.db.VMEntityVO; diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java index a29fa9f4e92..05c39bc0e48 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java @@ -1,3 +1,19 @@ +// 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.engine.cloud.entity.api; import org.apache.cloudstack.engine.cloud.entity.api.db.VMEntityVO; diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java index fba899e04b5..2d915d9abf9 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java @@ -1,3 +1,19 @@ +// 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.engine.cloud.entity.api; import java.lang.reflect.Method; diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/ClusterEntityImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/ClusterEntityImpl.java index 20ffcb198c2..4ce69b529c3 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/ClusterEntityImpl.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/ClusterEntityImpl.java @@ -1,3 +1,19 @@ +// 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.engine.datacenter.entity.api; diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/DataCenterResourceManager.java b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/DataCenterResourceManager.java index f01db7e7cc2..953dc9a2a1a 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/DataCenterResourceManager.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/DataCenterResourceManager.java @@ -1,3 +1,19 @@ +// 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.engine.datacenter.entity.api; diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/DataCenterResourceManagerImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/DataCenterResourceManagerImpl.java index 7a792c2c495..dc5b57f24bf 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/DataCenterResourceManagerImpl.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/DataCenterResourceManagerImpl.java @@ -1,3 +1,19 @@ +// 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.engine.datacenter.entity.api; import javax.inject.Inject; diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/HostEntityImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/HostEntityImpl.java index 658a389a93d..1841889e9ed 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/HostEntityImpl.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/HostEntityImpl.java @@ -1,3 +1,19 @@ +// 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.engine.datacenter.entity.api; import java.lang.reflect.Method; diff --git a/engine/orchestration/test/org/apache/cloudstack/engine/provisioning/test/ChildTestConfiguration.java b/engine/orchestration/test/org/apache/cloudstack/engine/provisioning/test/ChildTestConfiguration.java index 72f956ecf15..86cd47830e6 100644 --- a/engine/orchestration/test/org/apache/cloudstack/engine/provisioning/test/ChildTestConfiguration.java +++ b/engine/orchestration/test/org/apache/cloudstack/engine/provisioning/test/ChildTestConfiguration.java @@ -1,3 +1,19 @@ +// 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.engine.provisioning.test; diff --git a/engine/orchestration/test/org/apache/cloudstack/engine/provisioning/test/ProvisioningTest.java b/engine/orchestration/test/org/apache/cloudstack/engine/provisioning/test/ProvisioningTest.java index 70b5b93c4fb..eaff4269a0b 100644 --- a/engine/orchestration/test/org/apache/cloudstack/engine/provisioning/test/ProvisioningTest.java +++ b/engine/orchestration/test/org/apache/cloudstack/engine/provisioning/test/ProvisioningTest.java @@ -1,3 +1,19 @@ +// 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. /** * */ diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/lifecycle/DefaultImageDataStoreLifeCycle.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/lifecycle/DefaultImageDataStoreLifeCycle.java index 071e175c914..3ced8d3b8d2 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/lifecycle/DefaultImageDataStoreLifeCycle.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/lifecycle/DefaultImageDataStoreLifeCycle.java @@ -1,3 +1,19 @@ +// 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.storage.image.store.lifecycle; import java.util.Map; diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/AopTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/AopTest.java index 0c2a2adf061..bde5804e624 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/AopTest.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/AopTest.java @@ -1,3 +1,19 @@ +// 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.storage.test; import static java.lang.annotation.ElementType.METHOD; diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/AopTestAdvice.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/AopTestAdvice.java index ba356e3e6b5..63669c453d7 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/AopTestAdvice.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/AopTestAdvice.java @@ -1,3 +1,19 @@ +// 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.storage.test; import org.aspectj.lang.ProceedingJoinPoint; diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java index 69907bc790b..1b12b54e024 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java @@ -1,3 +1,19 @@ +// 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.storage.test; import org.apache.cloudstack.storage.HostEndpointRpcServer; diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/CloudStackTestNGBase.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/CloudStackTestNGBase.java index 5bc7c0d2d27..dc7223c9e84 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/CloudStackTestNGBase.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/CloudStackTestNGBase.java @@ -1,3 +1,19 @@ +// 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.storage.test; import java.lang.reflect.Method; diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockRpcCallBack.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockRpcCallBack.java index 294cb1ed8fa..207cc52e989 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockRpcCallBack.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockRpcCallBack.java @@ -1,3 +1,19 @@ +// 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.storage.test; import javax.inject.Inject; diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/StorageFactoryBean.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/StorageFactoryBean.java index 68952b17f9e..2ac6dac4c16 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/StorageFactoryBean.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/StorageFactoryBean.java @@ -1,3 +1,19 @@ +// 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.storage.test; diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/StorageTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/StorageTest.java index 2a285cb8f41..0ee7fe0a431 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/StorageTest.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/StorageTest.java @@ -1,3 +1,19 @@ +// 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.storage.test; import static org.junit.Assert.*; diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TestConfiguration.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TestConfiguration.java index de7944ce8ac..d3280c0e38d 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TestConfiguration.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TestConfiguration.java @@ -1,3 +1,19 @@ +// 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.storage.test; import org.springframework.context.annotation.Bean; diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TestNG.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TestNG.java index 3da31e8eb4b..b3ecd3c22cb 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TestNG.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TestNG.java @@ -1,3 +1,19 @@ +// 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.storage.test; import junit.framework.Assert; diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TestNGAop.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TestNGAop.java index 6cc2d209c20..130ecd21980 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TestNGAop.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/TestNGAop.java @@ -1,3 +1,19 @@ +// 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.storage.test; import java.lang.reflect.Method; diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/XenEndpoint.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/XenEndpoint.java index a96d7eca154..d0709d5be0f 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/XenEndpoint.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/XenEndpoint.java @@ -1,3 +1,19 @@ +// 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.storage.test; public class XenEndpoint { diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java index 971e9a5503b..80b1918665d 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java @@ -1,3 +1,19 @@ +// 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.storage.snapshot; import org.apache.cloudstack.engine.cloud.entity.api.SnapshotEntity; diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/HypervisorBasedSnapshot.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/HypervisorBasedSnapshot.java index 7df413f9ab6..7f18200cd3d 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/HypervisorBasedSnapshot.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/HypervisorBasedSnapshot.java @@ -1,3 +1,19 @@ +// 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.storage.snapshot.strategy; import org.apache.cloudstack.storage.snapshot.SnapshotInfo; diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/StorageBasedSnapshot.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/StorageBasedSnapshot.java index 42807d6f738..fa9c5aeaa08 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/StorageBasedSnapshot.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/StorageBasedSnapshot.java @@ -1,3 +1,19 @@ +// 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.storage.snapshot.strategy; import org.apache.cloudstack.storage.snapshot.SnapshotInfo; diff --git a/engine/storage/src/org/apache/cloudstack/storage/EndPoint.java b/engine/storage/src/org/apache/cloudstack/storage/EndPoint.java index e92877c6a84..cdc66269a99 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/EndPoint.java +++ b/engine/storage/src/org/apache/cloudstack/storage/EndPoint.java @@ -1,3 +1,19 @@ +// 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.storage; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; diff --git a/engine/storage/src/org/apache/cloudstack/storage/backup/SnapshotOnBackupStoreInfo.java b/engine/storage/src/org/apache/cloudstack/storage/backup/SnapshotOnBackupStoreInfo.java index d01f2b43131..5f5ce2f4a92 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/backup/SnapshotOnBackupStoreInfo.java +++ b/engine/storage/src/org/apache/cloudstack/storage/backup/SnapshotOnBackupStoreInfo.java @@ -1,3 +1,19 @@ +// 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.storage.backup; import org.apache.cloudstack.storage.backup.datastore.BackupStoreInfo; diff --git a/engine/storage/src/org/apache/cloudstack/storage/backup/datastore/BackupStoreInfo.java b/engine/storage/src/org/apache/cloudstack/storage/backup/datastore/BackupStoreInfo.java index 2c126cf555f..ca1454af570 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/backup/datastore/BackupStoreInfo.java +++ b/engine/storage/src/org/apache/cloudstack/storage/backup/datastore/BackupStoreInfo.java @@ -1,3 +1,19 @@ +// 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.storage.backup.datastore; import org.apache.cloudstack.storage.backup.SnapshotOnBackupStoreInfo; diff --git a/engine/storage/src/org/apache/cloudstack/storage/command/CopyCmd.java b/engine/storage/src/org/apache/cloudstack/storage/command/CopyCmd.java index 42eaa2fbce2..dcb81ba486c 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/command/CopyCmd.java +++ b/engine/storage/src/org/apache/cloudstack/storage/command/CopyCmd.java @@ -1,3 +1,19 @@ +// 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.storage.command; import org.apache.cloudstack.storage.to.ImageOnPrimayDataStoreTO; diff --git a/engine/storage/src/org/apache/cloudstack/storage/command/CopyTemplateToPrimaryStorageAnswer.java b/engine/storage/src/org/apache/cloudstack/storage/command/CopyTemplateToPrimaryStorageAnswer.java index 773a3e4a07f..9fd9317c38f 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/command/CopyTemplateToPrimaryStorageAnswer.java +++ b/engine/storage/src/org/apache/cloudstack/storage/command/CopyTemplateToPrimaryStorageAnswer.java @@ -1,3 +1,19 @@ +// 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.storage.command; import com.cloud.agent.api.Answer; diff --git a/engine/storage/src/org/apache/cloudstack/storage/command/CreatePrimaryDataStoreCmd.java b/engine/storage/src/org/apache/cloudstack/storage/command/CreatePrimaryDataStoreCmd.java index 5a64e334bee..c9808d904ae 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/command/CreatePrimaryDataStoreCmd.java +++ b/engine/storage/src/org/apache/cloudstack/storage/command/CreatePrimaryDataStoreCmd.java @@ -1,3 +1,19 @@ +// 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.storage.command; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo; diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/DataStore.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/DataStore.java index df21b6e96a4..90e0cb64228 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/DataStore.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/DataStore.java @@ -1,3 +1,19 @@ +// 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.storage.datastore; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManager.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManager.java index 7bca13b8587..2c2738f5a40 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManager.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManager.java @@ -1,3 +1,19 @@ +// 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.storage.datastore; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java index 7c310b777f3..0607e1ac8a5 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java @@ -1,3 +1,19 @@ +// 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.storage.datastore; import javax.inject.Inject; diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/TemplateInDataStore.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/TemplateInDataStore.java index f3697cf5a96..03c0a07630a 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/TemplateInDataStore.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/TemplateInDataStore.java @@ -1,3 +1,19 @@ +// 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.storage.datastore; import org.apache.cloudstack.engine.subsystem.api.storage.disktype.VolumeDiskType; diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/protocol/DataStoreProtocol.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/protocol/DataStoreProtocol.java index 54518ae3a5b..b0a7d50c57d 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/protocol/DataStoreProtocol.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/protocol/DataStoreProtocol.java @@ -1,3 +1,19 @@ +// 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.storage.datastore.protocol; public enum DataStoreProtocol { diff --git a/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreDao.java b/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreDao.java index 13d8132da86..08f9182f237 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreDao.java +++ b/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreDao.java @@ -1,3 +1,19 @@ +// 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.storage.db; import org.apache.cloudstack.storage.volume.ObjectInDataStoreStateMachine; diff --git a/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreDaoImpl.java b/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreDaoImpl.java index 4b0b2ca4b6c..ac75a9abed6 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreDaoImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreDaoImpl.java @@ -1,3 +1,19 @@ +// 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.storage.db; import org.springframework.stereotype.Component; diff --git a/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreVO.java b/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreVO.java index 2b92f6df827..c6bacbd9078 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreVO.java +++ b/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreVO.java @@ -1,3 +1,19 @@ +// 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.storage.db; import java.util.Date; diff --git a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotEntityImpl.java b/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotEntityImpl.java index d57d078cb52..1363251ed95 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotEntityImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotEntityImpl.java @@ -1,3 +1,19 @@ +// 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.storage.snapshot; import java.lang.reflect.Method; diff --git a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotInfo.java b/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotInfo.java index 478fe3dd78b..1c572cf3b25 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotInfo.java +++ b/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotInfo.java @@ -1,3 +1,19 @@ +// 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.storage.snapshot; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; diff --git a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotService.java b/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotService.java index bc56e6287a0..d50c9a0c8f3 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotService.java +++ b/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotService.java @@ -1,3 +1,19 @@ +// 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.storage.snapshot; import org.apache.cloudstack.engine.cloud.entity.api.SnapshotEntity; diff --git a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategy.java b/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategy.java index 980b2ddb970..4e311862e50 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategy.java +++ b/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategy.java @@ -1,3 +1,19 @@ +// 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.storage.snapshot; public interface SnapshotStrategy { diff --git a/engine/storage/src/org/apache/cloudstack/storage/to/ImageDataStoreTO.java b/engine/storage/src/org/apache/cloudstack/storage/to/ImageDataStoreTO.java index 9fd335ab9ce..43998a30102 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/to/ImageDataStoreTO.java +++ b/engine/storage/src/org/apache/cloudstack/storage/to/ImageDataStoreTO.java @@ -1,3 +1,19 @@ +// 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.storage.to; import org.apache.cloudstack.storage.image.store.ImageDataStoreInfo; diff --git a/engine/storage/src/org/apache/cloudstack/storage/to/NfsPrimaryDataStoreTO.java b/engine/storage/src/org/apache/cloudstack/storage/to/NfsPrimaryDataStoreTO.java index 06ff16b2bfe..96fb6bb2401 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/to/NfsPrimaryDataStoreTO.java +++ b/engine/storage/src/org/apache/cloudstack/storage/to/NfsPrimaryDataStoreTO.java @@ -1,3 +1,19 @@ +// 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.storage.to; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo; diff --git a/engine/storage/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java b/engine/storage/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java index 13d51acb7a1..cd67b97b02c 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java +++ b/engine/storage/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java @@ -1,3 +1,19 @@ +// 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.storage.to; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo; diff --git a/engine/storage/src/org/apache/cloudstack/storage/to/TemplateTO.java b/engine/storage/src/org/apache/cloudstack/storage/to/TemplateTO.java index e0d18dbdc48..b9db8cc95ba 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/to/TemplateTO.java +++ b/engine/storage/src/org/apache/cloudstack/storage/to/TemplateTO.java @@ -1,3 +1,19 @@ +// 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.storage.to; import org.apache.cloudstack.engine.subsystem.api.storage.disktype.VolumeDiskType; diff --git a/engine/storage/src/org/apache/cloudstack/storage/to/VolumeTO.java b/engine/storage/src/org/apache/cloudstack/storage/to/VolumeTO.java index 8ec117c605d..af71344bf33 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/to/VolumeTO.java +++ b/engine/storage/src/org/apache/cloudstack/storage/to/VolumeTO.java @@ -1,3 +1,19 @@ +// 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.storage.to; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/DefaultPrimaryDataStore.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/DefaultPrimaryDataStore.java index a4bcdf3ba18..ff95aa0624e 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/DefaultPrimaryDataStore.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/DefaultPrimaryDataStore.java @@ -1,3 +1,19 @@ +// 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.storage.datastore; import java.util.ArrayList; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/AbstractPrimaryDataStoreConfigurator.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/AbstractPrimaryDataStoreConfigurator.java index db1fbde977c..2ecbfbf8ad6 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/AbstractPrimaryDataStoreConfigurator.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/AbstractPrimaryDataStoreConfigurator.java @@ -1,3 +1,19 @@ +// 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.storage.datastore.configurator; import javax.inject.Inject; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/xen/AbstractXenConfigurator.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/xen/AbstractXenConfigurator.java index b8efb482c0d..1181dea50df 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/xen/AbstractXenConfigurator.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/configurator/xen/AbstractXenConfigurator.java @@ -1,3 +1,19 @@ +// 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.storage.datastore.configurator.xen; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/DefaultPrimaryDataStoreDriverImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/DefaultPrimaryDataStoreDriverImpl.java index ee0bcc3a06d..2855d4e96f9 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/DefaultPrimaryDataStoreDriverImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/DefaultPrimaryDataStoreDriverImpl.java @@ -1,3 +1,19 @@ +// 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.storage.datastore.driver; import java.util.List; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/PrimaryDataStoreDriver.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/PrimaryDataStoreDriver.java index 57b3a3079d8..3a9b13d5422 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/PrimaryDataStoreDriver.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/PrimaryDataStoreDriver.java @@ -1,3 +1,19 @@ +// 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.storage.datastore.driver; import java.util.Map; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultPrimaryDatastoreProviderImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultPrimaryDatastoreProviderImpl.java index dae0832cf55..55966be5ac1 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultPrimaryDatastoreProviderImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultPrimaryDatastoreProviderImpl.java @@ -1,3 +1,19 @@ +// 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.storage.datastore.provider; import java.net.URI; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/PrimaryDataStoreProviderManager.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/PrimaryDataStoreProviderManager.java index dae9f1113b2..c77f7a32c1c 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/PrimaryDataStoreProviderManager.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/PrimaryDataStoreProviderManager.java @@ -1,3 +1,19 @@ +// 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.storage.datastore.provider; import java.util.List; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java index 4ec6fba9497..13ae35c6ec4 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java @@ -1,3 +1,19 @@ +// 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.storage.volume; import java.util.Date; diff --git a/framework/rest/test/org/apache/cloudstack/framework/ws/jackson/CSJacksonAnnotationTest.java b/framework/rest/test/org/apache/cloudstack/framework/ws/jackson/CSJacksonAnnotationTest.java index 52b2d7fb9c6..fef6ba28e33 100644 --- a/framework/rest/test/org/apache/cloudstack/framework/ws/jackson/CSJacksonAnnotationTest.java +++ b/framework/rest/test/org/apache/cloudstack/framework/ws/jackson/CSJacksonAnnotationTest.java @@ -1,3 +1,19 @@ +// 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.framework.ws.jackson; import java.io.IOException; diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java index afee8b03a96..e55cce0faae 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java @@ -1,3 +1,19 @@ +// 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.storage.datastore.driver; import java.util.Map; diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidfirePrimaryDataStoreProvider.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidfirePrimaryDataStoreProvider.java index d84ac8ead24..bcffbd37e4b 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidfirePrimaryDataStoreProvider.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/provider/SolidfirePrimaryDataStoreProvider.java @@ -1,3 +1,19 @@ +// 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.storage.datastore.provider; import java.util.List; diff --git a/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/AopTestAdvice.java b/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/AopTestAdvice.java index ba356e3e6b5..63669c453d7 100644 --- a/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/AopTestAdvice.java +++ b/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/AopTestAdvice.java @@ -1,3 +1,19 @@ +// 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.storage.test; import org.aspectj.lang.ProceedingJoinPoint; diff --git a/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java b/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java index 6a7b5ad16b1..eb6fe453886 100644 --- a/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java +++ b/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java @@ -1,3 +1,19 @@ +// 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.storage.test; import org.apache.cloudstack.storage.image.motion.ImageMotionService; diff --git a/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/TestConfiguration.java b/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/TestConfiguration.java index 42cd8fb5f59..2c6092d7408 100644 --- a/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/TestConfiguration.java +++ b/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/TestConfiguration.java @@ -1,3 +1,19 @@ +// 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.storage.test; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; diff --git a/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/VolumeTest.java b/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/VolumeTest.java index 50a43141f15..f5035bf4303 100644 --- a/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/VolumeTest.java +++ b/plugins/storage/volume/solidfire/test/org/apache/cloudstack/storage/test/VolumeTest.java @@ -1,3 +1,19 @@ +// 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.storage.test; import static org.junit.Assert.*; From ea3f5ecb54ce6ab0029c994f06aea695c866fa6e Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Sat, 12 Jan 2013 06:31:47 -0800 Subject: [PATCH 68/73] Fix license for xml files in javelin Signed-off-by: Rohit Yadav --- .../test/resource/provisioningContext.xml | 18 ++++++++++++++++++ engine/service/pom.xml | 18 ++++++++++++++++++ .../service/src/main/webapp/WEB-INF/beans.xml | 18 ++++++++++++++++++ .../service/src/main/webapp/WEB-INF/log4j.xml | 18 ++++++++++++++++++ engine/service/src/main/webapp/WEB-INF/web.xml | 18 ++++++++++++++++++ .../test/resource/storageContext.xml | 18 ++++++++++++++++++ .../integration-test/test/resource/testng.xml | 18 ++++++++++++++++++ .../volume/test/resource/testContext.xml | 18 ++++++++++++++++++ .../SampleManagementServerAppContext.xml | 18 ++++++++++++++++++ framework/jobs/pom.xml | 18 ++++++++++++++++++ framework/rest/pom.xml | 18 ++++++++++++++++++ .../solidfire/test/resource/storageContext.xml | 18 ++++++++++++++++++ .../com/cloud/utils/QualifierTestContext.xml | 18 ++++++++++++++++++ .../utils/db/transactionContextBuilderTest.xml | 18 ++++++++++++++++++ 14 files changed, 252 insertions(+) mode change 100755 => 100644 engine/service/src/main/webapp/WEB-INF/beans.xml mode change 100755 => 100644 engine/service/src/main/webapp/WEB-INF/log4j.xml mode change 100755 => 100644 framework/jobs/pom.xml diff --git a/engine/orchestration/test/resource/provisioningContext.xml b/engine/orchestration/test/resource/provisioningContext.xml index a5a9560a935..6ed0ab5d472 100644 --- a/engine/orchestration/test/resource/provisioningContext.xml +++ b/engine/orchestration/test/resource/provisioningContext.xml @@ -1,3 +1,21 @@ + diff --git a/engine/service/src/main/webapp/WEB-INF/web.xml b/engine/service/src/main/webapp/WEB-INF/web.xml index 71c1ef38329..6b8648a5b33 100644 --- a/engine/service/src/main/webapp/WEB-INF/web.xml +++ b/engine/service/src/main/webapp/WEB-INF/web.xml @@ -1,3 +1,21 @@ + diff --git a/engine/storage/integration-test/test/resource/storageContext.xml b/engine/storage/integration-test/test/resource/storageContext.xml index f5e343e5850..c81fe7d8ee9 100644 --- a/engine/storage/integration-test/test/resource/storageContext.xml +++ b/engine/storage/integration-test/test/resource/storageContext.xml @@ -1,3 +1,21 @@ + diff --git a/engine/storage/volume/test/resource/testContext.xml b/engine/storage/volume/test/resource/testContext.xml index 83fe842c722..67f242273f3 100644 --- a/engine/storage/volume/test/resource/testContext.xml +++ b/engine/storage/volume/test/resource/testContext.xml @@ -1,3 +1,21 @@ + 4.0.0 org.apache.cloudstack diff --git a/framework/rest/pom.xml b/framework/rest/pom.xml index e9009bf72d1..2f890562190 100644 --- a/framework/rest/pom.xml +++ b/framework/rest/pom.xml @@ -1,3 +1,21 @@ + 4.0.0 diff --git a/plugins/storage/volume/solidfire/test/resource/storageContext.xml b/plugins/storage/volume/solidfire/test/resource/storageContext.xml index 6800d8f48b8..e4ba9867803 100644 --- a/plugins/storage/volume/solidfire/test/resource/storageContext.xml +++ b/plugins/storage/volume/solidfire/test/resource/storageContext.xml @@ -1,3 +1,21 @@ + Date: Sat, 12 Jan 2013 06:34:34 -0800 Subject: [PATCH 69/73] Fix license on xml.in, jsp and ucls files on javelin Signed-off-by: Rohit Yadav --- client/tomcatconf/applicationContext.xml.in | 18 ++++++++++++++++++ client/tomcatconf/componentContext.xml.in | 18 ++++++++++++++++++ engine/service/src/main/webapp/index.jsp | 18 ++++++++++++++++++ engine/storage/storage.ucls | 18 ++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in index 34bb853c52e..2a367f64280 100644 --- a/client/tomcatconf/applicationContext.xml.in +++ b/client/tomcatconf/applicationContext.xml.in @@ -1,3 +1,21 @@ +

Hello World!

diff --git a/engine/storage/storage.ucls b/engine/storage/storage.ucls index 9b3a47ce3f7..23a7b21fe00 100644 --- a/engine/storage/storage.ucls +++ b/engine/storage/storage.ucls @@ -1,3 +1,21 @@ + Date: Mon, 14 Jan 2013 10:16:07 -0800 Subject: [PATCH 70/73] Fix POM that breaks the build --- engine/pom.xml | 4 ++-- engine/service/pom.xml | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/engine/pom.xml b/engine/pom.xml index e1681f1b719..0aca70ef98b 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -1,4 +1,3 @@ - +--> + 4.0.0 cloud-engine Apache CloudStack Cloud Engine diff --git a/engine/service/pom.xml b/engine/service/pom.xml index fa8826bfce1..cd4e9531d28 100644 --- a/engine/service/pom.xml +++ b/engine/service/pom.xml @@ -16,7 +16,6 @@ specific language governing permissions and limitations under the License. --> - From 64c947a9f8a2b47c356f399d755fdf969f377cde Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Mon, 14 Jan 2013 10:52:37 -0800 Subject: [PATCH 71/73] Re-fix startup of management server --- .../cloud/servlet/CloudStartupServlet.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/server/src/com/cloud/servlet/CloudStartupServlet.java b/server/src/com/cloud/servlet/CloudStartupServlet.java index eae211b63f7..d3b930d9f24 100755 --- a/server/src/com/cloud/servlet/CloudStartupServlet.java +++ b/server/src/com/cloud/servlet/CloudStartupServlet.java @@ -16,17 +16,22 @@ // under the License. package com.cloud.servlet; +import java.io.File; + import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; +import org.apache.log4j.xml.DOMConfigurator; import com.cloud.api.ApiServer; import com.cloud.exception.InvalidParameterValueException; import com.cloud.server.ConfigurationServer; import com.cloud.server.ManagementServer; +import com.cloud.utils.PropertiesUtil; import com.cloud.utils.SerialVersionUID; import com.cloud.utils.component.ComponentContext; @@ -37,10 +42,12 @@ public class CloudStartupServlet extends HttpServlet implements ServletContextLi @Override public void init() throws ServletException { + initLog4j(); ConfigurationServer c = (ConfigurationServer)ComponentContext.getComponent(ConfigurationServer.Name); try { c.persistDefaultValues(); ManagementServer ms = (ManagementServer)ComponentContext.getComponent(ManagementServer.Name); + ms.startup(); ms.enableAdminUser("password"); ApiServer.initApiServer(); } catch (InvalidParameterValueException ipve) { @@ -65,4 +72,18 @@ public class CloudStartupServlet extends HttpServlet implements ServletContextLi @Override public void contextDestroyed(ServletContextEvent sce) { } + + private void initLog4j() { + File file = PropertiesUtil.findConfigFile("log4j-cloud.xml"); + if (file != null) { + s_logger.info("log4j configuration found at " + file.getAbsolutePath()); + DOMConfigurator.configureAndWatch(file.getAbsolutePath()); + } else { + file = PropertiesUtil.findConfigFile("log4j-cloud.properties"); + if (file != null) { + s_logger.info("log4j configuration found at " + file.getAbsolutePath()); + PropertyConfigurator.configureAndWatch(file.getAbsolutePath()); + } + } + } } From 6dfbcee63da5ae9321677a7440c426a023db5f0d Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Mon, 14 Jan 2013 13:57:30 -0800 Subject: [PATCH 72/73] Fix singleton initialization in ApiServer/ApiDispatcher to make it work under Spring bootstraped environment --- client/tomcatconf/applicationContext.xml.in | 1 + server/src/com/cloud/api/ApiDispatcher.java | 14 ++++++++----- server/src/com/cloud/api/ApiServer.java | 20 +++++++------------ server/src/com/cloud/api/ApiServlet.java | 6 +++++- .../cloud/projects/ProjectManagerImpl.java | 4 +++- .../com/cloud/vm/dao/DomainRouterDaoImpl.java | 1 + 6 files changed, 26 insertions(+), 20 deletions(-) diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in index 2a367f64280..186a1685fbd 100644 --- a/client/tomcatconf/applicationContext.xml.in +++ b/client/tomcatconf/applicationContext.xml.in @@ -50,6 +50,7 @@ + diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java index 67aa454ab2c..0df37f154fc 100755 --- a/server/src/com/cloud/api/ApiDispatcher.java +++ b/server/src/com/cloud/api/ApiDispatcher.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.StringTokenizer; import java.util.regex.Matcher; +import javax.annotation.PostConstruct; import javax.inject.Inject; import org.apache.cloudstack.acl.ControlledEntity; @@ -65,8 +66,8 @@ import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.UserContext; import com.cloud.utils.DateUtil; -import com.cloud.utils.NumbersUtil; import com.cloud.utils.ReflectUtil; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CSExceptionErrorCode; import com.cloud.utils.exception.CloudRuntimeException; @@ -85,9 +86,12 @@ public class ApiDispatcher { return s_instance; } - protected ApiDispatcher() { - super(); - s_instance = this; + public ApiDispatcher() { + } + + @PostConstruct + void init() { + s_instance = this; } public void setCreateSnapshotQueueSizeLimit(Long snapshotLimit) { @@ -475,7 +479,7 @@ public class ApiDispatcher { } //check access on the entities. - s_instance.doAccessChecks(cmd, entitiesToAccess); + getInstance().doAccessChecks(cmd, entitiesToAccess); } diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index e5b57e5afb4..8a2c6755ef0 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -45,6 +45,7 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import javax.annotation.PostConstruct; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.inject.Inject; @@ -131,7 +132,6 @@ import com.cloud.utils.Pair; import com.cloud.utils.NumbersUtil; import com.cloud.utils.ReflectUtil; import com.cloud.utils.StringUtils; -import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.PluggableService; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.SearchCriteria; @@ -165,22 +165,16 @@ public class ApiServer implements HttpRequestHandler { private static ExecutorService _executor = new ThreadPoolExecutor(10, 150, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(), new NamedThreadFactory("ApiServer")); - protected ApiServer() { - super(); + public ApiServer() { } - - public static void initApiServer() { - if (s_instance == null) { - s_instance = new ApiServer(); - s_instance = ComponentContext.inject(s_instance); - s_instance.init(); - } + + @PostConstruct + void initComponent() { + s_instance = this; + init(); } public static ApiServer getInstance() { - if (s_instance == null) { - ApiServer.initApiServer(); - } return s_instance; } diff --git a/server/src/com/cloud/api/ApiServlet.java b/server/src/com/cloud/api/ApiServlet.java index 1a8a04ee237..e535030f26c 100755 --- a/server/src/com/cloud/api/ApiServlet.java +++ b/server/src/com/cloud/api/ApiServlet.java @@ -32,25 +32,29 @@ import javax.servlet.http.HttpSession; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.ServerApiException; import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.exception.CloudAuthenticationException; import com.cloud.user.Account; import com.cloud.user.AccountService; import com.cloud.user.UserContext; import com.cloud.utils.StringUtils; +import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CloudRuntimeException; +@Component("apiServlet") @SuppressWarnings("serial") public class ApiServlet extends HttpServlet { public static final Logger s_logger = Logger.getLogger(ApiServlet.class.getName()); private static final Logger s_accessLogger = Logger.getLogger("apiserver." + ApiServer.class.getName()); ApiServer _apiServer; - @Inject AccountService _accountMgr; + AccountService _accountMgr; public ApiServlet() { super(); _apiServer = ApiServer.getInstance(); + _accountMgr = ComponentContext.getComponent(AccountService.class); if (_apiServer == null) { throw new CloudRuntimeException("ApiServer not initialized"); } diff --git a/server/src/com/cloud/projects/ProjectManagerImpl.java b/server/src/com/cloud/projects/ProjectManagerImpl.java index 00b7716f19c..ebe6d0c464f 100755 --- a/server/src/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/com/cloud/projects/ProjectManagerImpl.java @@ -131,7 +131,9 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ Map configs = _configDao.getConfiguration(params); _invitationRequired = Boolean.valueOf(configs.get(Config.ProjectInviteRequired.key())); - _invitationTimeOut = Long.valueOf(configs.get(Config.ProjectInvitationExpirationTime.key()))*1000; + + String value = configs.get(Config.ProjectInvitationExpirationTime.key()); + _invitationTimeOut = Long.valueOf(value != null ? value : "86400")*1000; _allowUserToCreateProject = Boolean.valueOf(configs.get(Config.AllowUserToCreateProject.key())); diff --git a/server/src/com/cloud/vm/dao/DomainRouterDaoImpl.java b/server/src/com/cloud/vm/dao/DomainRouterDaoImpl.java index 40c97e1e278..cfe9f43f5f3 100755 --- a/server/src/com/cloud/vm/dao/DomainRouterDaoImpl.java +++ b/server/src/com/cloud/vm/dao/DomainRouterDaoImpl.java @@ -49,6 +49,7 @@ import com.cloud.vm.VirtualMachine.State; @Component @Local(value = { DomainRouterDao.class }) +@DB public class DomainRouterDaoImpl extends GenericDaoBase implements DomainRouterDao { protected SearchBuilder AllFieldsSearch; From 96bd1d4172aaa37757cc75a694b40783a6b962c4 Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Mon, 14 Jan 2013 14:10:47 -0800 Subject: [PATCH 73/73] Forget to save changed file in last commit --- server/src/com/cloud/servlet/CloudStartupServlet.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/src/com/cloud/servlet/CloudStartupServlet.java b/server/src/com/cloud/servlet/CloudStartupServlet.java index d3b930d9f24..bbcf3535300 100755 --- a/server/src/com/cloud/servlet/CloudStartupServlet.java +++ b/server/src/com/cloud/servlet/CloudStartupServlet.java @@ -27,7 +27,6 @@ import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.xml.DOMConfigurator; -import com.cloud.api.ApiServer; import com.cloud.exception.InvalidParameterValueException; import com.cloud.server.ConfigurationServer; import com.cloud.server.ManagementServer; @@ -49,7 +48,7 @@ public class CloudStartupServlet extends HttpServlet implements ServletContextLi ManagementServer ms = (ManagementServer)ComponentContext.getComponent(ManagementServer.Name); ms.startup(); ms.enableAdminUser("password"); - ApiServer.initApiServer(); + //ApiServer.initApiServer(); } catch (InvalidParameterValueException ipve) { s_logger.error("Exception starting management server ", ipve); throw new ServletException (ipve.getMessage());