From 7c5c3c5dfcc985dfd6dd890c96fa840a2bac3e0e Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 9 Jan 2013 05:43:41 -0800 Subject: [PATCH 01/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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 zcmZs>V{~L)urM4a6HGj@ZQHi(Ol)&vbK*>F8y#B{PHfw@lehESd;fgvTdPm^Is5e9 zyIi%aDne0S0s$5q76b$YK}u3o83Y7u0(fb_KmkXBLS{;UKi?cAwVXjf;84FVP>}RY zOb`%45Ghe1RgcWGEO&3T#m1*@7J$z@+zDZTpzz&B1rug%5R)T=qplIpJ6X*6Q_3D0 z!-N-={2Aw8sk0Vi9>>3i#yy4^MvR8#f-pppuqS$}?gt+qOUX#{FfuS)k7I|-&Ic1~ zUKiPT=gWY@4W(eD{Dy|4qf$yzl#wEl>pT(Ot0!gO_bYczK;u^a3#cz>ZFD95X<-UsJGT2m@_dG!_C!C?Kj=7x(#v7*j*lb=@Q4gYz@~Le}VULnFn0(M8Qim>^U(k9(UJpTMx1B zsGNo=W1KHPtk68};qu+3VS5oncEE@oG(~(*epLD5cm2a{&ga|V`1p9LDmqaxZT+xQ zL{jmA*@DgTSSHo(l8SENQGlP?y_~N5Gby%?wx|8N_>1}dDKg()clI*{5XMi}hV68P zTdaiS(XHMfDtdY|Alj_#?9y7m^@G>&9h=4^14W&rl(h9Hz+)Nq{RrO3Wi~$xhwg@t zGyd7al`!ufTqb6wi@SYR;?IgXUB~|qpl8?pQn8K=i+a-Wyi_u+9K+No5qmS@Fu~Q2 znpE^v1D4I#iLZ8fSB6TlC&W<=-LwY(LTUJ4k~;E4n6zm35b#E~3nYrZh%amx%eg9B zob854iXsI)8bfdxFqCprL9jj+MYHf&N^j}2<8c&I(R!dGuSqu_b{}hj2ASgkK!tm}jwB6xL>+ZLgb??Ja zPg$HaImWF^Rh^<_w#pY1jPAD&JXLk|?MF`r$&v>n_INZLG383UQw+(pcTl+8_6fA>LER$#tvfAi%ohB`UGg zwLRYcx<5^?a~J~Bxx6b2XKgL0g8Jp1<6g?GsBt>|Bck&P(pJLgf7d?y;A?yPV(VoQ zoTf#kjUE1m;~5vSDlac{gam~H4~;c;Ys2c7G?Yw0fKm2rh%BDn%f{~yNvW>w{&{4< z1?ZOtq!@d-JLDFE*2Zz1TG?S4x8v6fe2KCR=m|iMxzI^q`X33s)>j01;g_~Ur zDE&)I4tfT=sK~DWsR$FJ%Hdf0#Ml~itH_25{Qj~gelcHKH5v~1(klEI&d*7|^$`~P zYOYush-rDA&9-Cd4t<8dVX+Q>Gt)|~j$W_ZVmb0Hu|;m7ZVxdw^E;}s#E#e8%>h!o zH3F|c>}!^X-@Bi20OYZkbN6Y+CH01eXd9#Jyth9o1G$Y}WQz-zDVIN(Xd-z{5DEJ4 zdLx!puYRSEe46m+RJ?P}+_u~9I_7n{ zG>yj1|7*d9oP4!ML!y--p`kR+SkJu!MM0EcW_9m|gx0`7^u#mPp5c=lbDBT&kp@+h znH|f|8ZMOY4`*0pO=D#2!o^-G26I^K&xbA1e{DIVqfj>At}%xmuU8hh#TBssrgmMY zs=;_@b9LBbSVyJvn6?8MhXmybXSV~bl3=;~3Zc?n2VYZ?Ou!2W^b#LR_*@|HT=2p| zWcVj;Z%2rfESi8CbGbSiV@Q50QRrV2Vq1}N{q_JAT+3oVBHYDH42f{nioM)=;gh|t z9pQYG%6EUu;leM%o=+$AuT?)Ie5MFS=%i;v&e`y8i2+t{P2L|v zRq8S&?CKX0+V{iuGYdzj`FprlBS~&`1n+Uzvz04;1>5G^cYFQbse!2)4p*^^=X>XN ztnR(hS(jgSI&4fL=GVI+%jU--Ket-6*4Q{=Eh<7UtV#WDC3st_A^gAH{&1X4^5tZ6 zVAHt0wjT314S$4NzM_bIPyBR${M}Q8Jo07og1mQBRA97B;`f!Q&zp+{7lU$iI`BCV z*IembU0st>QdmB3v~c;<``TjJYn>h zT3&9-;3tnKCw!o}U43k`a6oi!U@ls$VBv-k`czte3v@OFnnG)bk7JFk5lJnnf~TzZ zn8p4)Gfl)a`JSI{%UQ|3lb9&kP?0)Mcn=E4ZVj>gZZz%MoBJWfS@}cT%4#}O-Uyd{ zhvZ+WR4Z*-oK)d_Fft>f4#_Y+|DOn^Ugw8prN>lC9FTw$5t|ADNAJ}p5(p4g$cjcf zke|fVebH3~xRylKD!VH3_(99!h3n$pM#K^7;Hu~ns<42e62o(zXh@NS+A?a3A2ZC# z;mtbXE*7FDh~`FSyyeXik=ht$UJzWy64M69ndj|&AE4bieIj`#$-_T4_Y>VyZUzPL z``b@qNloGRw%w(t(V?%Eknf}>3CHt{W@zv5_>(InN(#OqRFH(0X-twYUXr;UXtcPw zaI4b^iukZBlzJ5F`=A4Cec@Lwhysu(9)5p+ap4_O%o>moi(6qx>~a&Zz@Coa^0rlC zcSJp?xA5TBcK!w(XeKjTq(+!HHdfA|uiYw)Ey z`h}y@W8lts0A{qoDy_74pOnJ7j3vF6FAV$;Q7(l4Zi)lq*#;^gei z<#L47+S*!LPHjph^Nf2#7GJ*o2whrUy}gy!f3~dAlkEeEhQ}|g=86y?m|HTRDESMG z2qLuF&+fWSTkCVA1OgoNOJMRTJG##FE6WDF1Y<33~9~{W*ttzG}_*OxK=!mku*8|d6yr4E)Eq{;@(?yS;gA3k&oOfI; zKz(lq&D=BNL=s;b`NmrJ@vg?(S)Ho)8Uv1$wgR1quV4jXI0vYxMJK`JHjs#8p~M4O ztebo~tr&YQ?1L1Pb=l*3JuCIn?O>V97Vx-fc@uI00VH?Qxb1*lCEvd3~|Qff%vn~r{(QZ+;$QO2+zk?t!1XjDk2ssctjbHw<*9f zV1(tD|FvI}(A^=K3#OGQ?)>I#jRh?&{dBj#d0ntcU&0NOYdD*MEWU~`ckNZ$!x)YF z(vU*}6a0#>P-!f0M!(44%%M9kEdTkWs8^d-Rq+R4H}svo`X&4&@?JLQ-W?l6+wE#T z1aW3f`IlK%;P_LJ`K58+6D|lgzfWMa5V)tEJp!R*bd=%8-oGj9+BbJy{!3qeTaa*5 zqij4r%r{|nT25u*={zGewSpr>Ca%_?uLvwh#LhjF;X!5k%~!zd=JEC+QtjpV+R|e_U&c!RdF$JX#>5Lv4i{ zzg@ZHgV;a+jKTW#`V0e}7p9++UQZ1#aya{_akB)ludkePzToFM(pSQA5@!QA-K4TY zA`nguC`~_5sP&p1VJ^flXW<(RF*Te+V_R7=xxm{q>XyVKVMW?3;?n$Obdk2!v$e;6 zxTO0FmrC-&=y@XvRZ-?_aC>TbVjG3X6kxx+va^LBC0&Dy0M2^;R#BNnLW zP`LAI7bjP&ekKw1IUSF`&juT5gAZ>cVNF+4$d8^soJp%)p3ne^SThTGW;?nBC6<{L z+e{8`Tu5l^iRxLdmn%=xa@N?Ka(j^; zJ2G-HbV=}h`LL@Vg)XKvQL)bQP}CFD?G^bidQZNQ$*tGOtWevM@+sV$eILLfKyVnB zw#Gw+g%?0}CV{)&_QzGioAh3eY$Q)fV?H_@kt5%Au052G3}2bK0F#9pmXUJB4^0Fy zJABJ1H%$aV=gja(2r4>bB3hPasOgf|JO^)RLGcm{g^6OHnX1}fjtzv|OcpmBi*Re` z5YCGe?ClrQE~(b#-4l0A-g~=h%g2B_jlg|1h^S6Pc+|j@-qN_$tE*ctr>nri z0P73+(3b45k+>cI=f)wo{i^GpFwijc?pzA|>xSYkU3)@n=DP-E=zfBCY<>@D+{6@T zTnerZgEnLO>CUN?%sw5kzFqi^)!a9b<*p~S2}7y>NOAEzt%SdN=M}mPB64~4l8NHD z5QNzl`V-Tfe+UKnM1tqKh+1#nCv187bpjT5Tj&kX2wm6l=CRaL#cj_=R0H{ZG<8i zquRX-46eU*N4G!wr8yoJtX3m&Vnum^HM~H3we&4kZspb-?gKiy7=(m)r4y{{a`4EDyfpq@CB{c6`lenK0qi z#L$Y)aJ@^>YV4}wa+hcGmFUxT@|;%7iNsiW2N?>(Mx=dw)b-Y^i3c(WDkM<(KgFjv zXi)tfIZCbX>4UZ0+WIezCq)FkYVB#aT&qv<4gNhVf{f~%dK0Z>(3s!@+!==_kmU0kF;0|nIBAX= zbBW)GwTW4YKdjs*B`SGOc+7JsPA_-PQ#4dG55fPD7Lp}yDS{G&=%|NHV2Ce8V8Dn7 z$X%QPO@UDO)nbrCCxeGD{d!Ym@@vKQDYX*Q_l|QF5qLM>`ovI>)LrH@79Y;C@96gH zMC>{2T@%ZGqIRG1s|FxE25)o9J8nPdL0$~p@D`(E^Yh4H2ADlOc!h%vg8c=N^ohtkq|3!u-%*hAM!z4n zc=tZE`SCwv4|N*K5KMAhzEii>k!#0P?QY_vBMpslu7Dwp9C?x~H#r@0xp3+a z2k!r+6J#wQ-$MdLWAF>!w7I?+1&^(*Zh=&hWd{@^!p&E`@O3}c-T+S&E3GWU!^4HS zxk!2#XlQ86hCPB*GFqdDajPi>EbhDc=Z*IW9S;kzX5+2VXF@?w_khb=t+d!YK71Ue>3&W3yVz@;fWSR&1-tj4e>5rn z=e)v7?B~bir{Qk3aAng39|Tad6Vk~s>|(?2_#GP>+7z4@CgA|TqYE+r`B!u$P(O~$ zBTq98tjIlOSSwV#5{nK{Obyu)Pbte-&W=z)WgW8+^9RgH(m?p&GE@{b0)Dw%KfNfV zPRzMbd=wW{KIQs@I(PL2u5rGhuRg!yJcE#uQ5b?=v1YSB+!l?0&^Kp97d`>`4kN_4 zRr2OQ6e7oOm&pYWtq3@TbZdksqj87OkB!R0(U|4%g0Xyuc6=Kaia|d&o||i3I+J&^ z@gRt;NRDv+v2A5((!Bcb9vly?^MSlw&McS_lgF8;k8!Eh62S*>8!jbjH2DYdID;?P zHz?GQSqCXpNJYf9r6zImn1>Nj!5WT-y+c9V4gXro@@%<2W(-3=Kn1kCslHm1#QqK~ z7|G`IiO=cPJeR$B1|?BAFZ=^HOa9y#hRWwvrNbV&tY4g&xHsnYXz0xDTt<2m6aw5g z^1+6L%^C?j5w|5uzeG`%n9drB9I$_@|G=^`WyO9y<>Ox4z11Uy%+{K$)5`bQwC>36 znz(y*?eYAmGls=thpQFj^!&WJNo0N`3AOj#$btZlY+R`07v-k3(pKb$n{*zN|Lmy4cNuP4=Wm(>lW?W72E-eB9z3q=w+kxBzd3N z*(VnTB4cQCB{dxd#YKTnVyR|St|#U$FV6!i34|Msu8I2mKlG<1_P?-o+%Oo;9Q>?K zu<3_CgoFQRz&~{y^~QnMSdstxA=55$`sm4&Hg1vrO%D+T9->y$mNGJM^H2;1JZd}u z3CgzyT4Yg46@e`i$+m7;#B-mYA$=sc+!DgOOEH7o`4trmK~Lb6E3Zl9S800a=Be1C z2Azet~fnyf;QZbpoBg?jah6dF*TR=aXmy8SHBn|Q)X ztBzA{c5*lS{k6EH?zHD8{^v2`@Md#2-+swBm4(Qt@*gL}s@=Ek7rDKIeI}Oy7~k8q zSJ@o?w6itbO0$qmQ5KOGO~wo~S#rokDl|8BHO4fwf8>x+T0dpWLqO}yY}j!U$zzE5 zs{}qheE156ev?`EfUw)Ftle5)j1tReq&(={4%}$vG|#2yM(Lt>w~*HKbYu>IR|6#0;Is@R4@}PhRsrz3wHJt}!3Unw+gYe<&@5T{ZY_$!{evj=NFm(UBliHQMNL*?JpR)FFU;b{T zz->WHVh_XOl*o0Yc5%zDm{a7T&M!E)Z|1a zp;F>>9^Mi)U$%# zOMu_?wDJqtkksp=@QTgS+R4F58tRpY2x=%pkz{sK(HhR4T^4T3JwOj0k3Tb^{FlC7 zW0s*XJG;!V zS5Hr`A;G@Zu+2F?aeDvev236_m@Uq*A&A5PJ8vrC>HggQb~f&;4){;}Rzgl`z>{R8?Bw*y*O1?aw8p;mplZ_uy5Q~b1otk`CMYv!Y~k#vBf_HhdNk1l)KTmyWdP}avgbgiEs==&>TTzyiJceaz) zfEKDA4xfdMmDzK7z=#1>kvT79*o%Zue-R^vk@|XQP%LN`cu?koB88!XKXKN-U&Uq*M zyf;hKmnampUGtT|B)f0C}8Z8j^={+I_= zszMr;9ZrUO;c=!gx&<7hL@x?y_cthkGVkt4#qFK+vAoRM*z)Azg^a8vVhIbQzkF!q zke}A;n3v4T`-`$fOGQNKD6xrJ^YcxIwx&B+N-Z_XtoNt7r*8_J8o17=;DRlWKT;r3 zf2gu|L!r#jl(@ADp4{)>tQ_n!iif*5I@QE*OSR{eLb*3R?7pu@ENh?tU2q*8&zSsz zTV{t6myVwWlh;?Mla8OWr<_YRE*1hke;Ns{5+!oBdxx@jo5v7?@h2-lSnJR3P1in` zoOz#?Sa{#3w;eL3985U6eeSUEeRst0z3(9PSGDFh4whZ6=YL!+mS_O%Zil}1>u?*E z>yO7fKjeRyAa~;kYQ))qtM{&ZBoMySOd2&}M$VWDgUJYytO#8_KfWGLqs7?#kgrIi z%#d2?#`wdILp~_#c)!o$*v@BSYFcN%$zNYz4+8=A6_fK2HA`fw_2<%P)!l$Y#o|gy ziNt;SI1`K{{xEE|u0W zJE*-xsPV{}Eq&COYiaXj=4@h5PJX@+Iqn`@y&oUX?b&ZBR79q^uFJ_j`i(Md(3@@3 zH8YYGhj8R*54su9cRy{D{zG5&c-r?S49Kv{5py|K&(`&dwlwHWd;YV&-PX0{`iS-q zT3~x5Hg?)?Q263ZXXMlCn&nKS_@;oyiL=X-Ou^47bNV*L!MGdS_pbTgd~ToFmP5eP zlmH}=Lhh$%dMt&(W(=P*sk`1rY#{pBQ!ZG-5Z%rP);05v_cuj7ow2VnW?p;E{-d8) zmxRmku%!8HHL=<6k-Wk;&VZf}I%D$VEEAJLfa9W&*>8N&<7;WlPkl*oaqgTmu7Oy) z_VkxfawvFAhmrr4YxmVe@KYR|h?t1HD^2u2N22+*)CJ1_NX1f@l<>Ab5XO^Q;7@!4 zm79A5egp;41!f^Yd1VMY{S&|+!o`v6P3E{BBqCtActrZC#AKaY4qINF|M%WlK?#eg zu5NCg!)?()zHdq`Au)4HWBTlR4PXKK$!5>Ut``@F`OB`ly3`|GWK8f`M;k^nq2(}u z$MYy-w$^>Pz2!a3oso(!etXuO1#(G8nV`DvD(kZ=1X-#MKF0^W*}QLiT!JgJn*ejv zZxeXfsz|HT&>eRzt2OQy@_q;goaBe5(6-%NeC{;S4Cdo+K*R52JnBy57P8tsT~tLw zbYSd4Cb@hkLh5TdB4gu957E+f|V?q_$dFR|w!kKz~cfRVPP(puNprmk9 z83v`bhIQJlU@KLpn7^lkCx{Ck+rOA`3g9rc#$c?C^ zq}+oq153a!$SX=lY`zfEp#R|BuZy6d0B{gO#wt8Erl74_ z2xuBmGExJ}^5fU$AQBQ(h9?oL5AR8&VB=#X_DK8VjgNV7Yl>} z&xIkHr~_K6XWO||M$CzRS4i?jSnG^!F1Y6#1y=lJ>w=7%|6O2SFP^D~LB#}zkAAwpecnjPAE&Q=1wEzHc$l9u z5oUz>wSi1RPhKyX(YUYqKx$dB5Y%|YB94MbcivP)Q+Z`{U)DR1E!G&B1SXgBSwF3p zt6w|=^F`|R+n=3INnTw(;NY~}3{Oc|FDr-~O(8*z601 z8u(CfWN=(6_=ITld^vFLZbLMBuB}p;RV^@D~UJe~T zZr^)cKX-U^;`3%tHBJ2PU;uV|#5xeG6-42Yg{{Yc7!jZ2IyW1_2+3H3xQmgIBoI=J z#1O&5Qde-blz^_bbPY&kD7j_`%jH9`8X0w^#Hq)nxG&DoaK1G<=>W{N1bixSQxZ#uJ zKLC9GG7tJV^Y_F3xt#$p%r>PiU3o4tPtjiGgPO_fHFK;Kh6Z*1VAgTF$`H5&A++1$ zwZk{-x05~W8bRQFzP93j<*c0CqiH3Y3?wr~(aO-sQcB5NJZOA0KjMA}m~FAwWmB-z z9pCs3k{6!vx7exJHJFUAfAHK3lb5idWIj%ko?5>q#gNcnU~A~|cKh-5>e>6rC-2<+ zFWXI5oFplqu-8DE;o`&}{jMAEhE&cNj^mC8Ef%W8>VaKYX14k9Xrd8PacyKlv4XJ1 zXExQ0U92QiirfC?)c5VNBy^3N8wGt6b$w#2Z}R`r#XEU=E=1-#T35r!$kYAlOFyo- zd|lQpToh0j{KHxS<}S;as&XVmRNvH;XFh-uZurhKhT(PcE* z^4t*`Bz_Dg$7xp8`CZ7$ct=fF4!ab%#1T;!U0j65#-p~F^74Vfq22pm-=5xqHzcBQ z2ZLrEec}yzc|~R4K+H`@%P)#HstUC7BO0I_8yj-1RC^w-+N zWsTs~b6d;4#g38T9m@Exy!`ULI|8ivy!U0RixIb$X2$N)UcyC)&Y-%i?)A(ikxE-( zV=ghk?FPL+d4ukpL6JZ|2&InCy$JJJsnd`EcAiRk1(z4bWj|*bl*ISzZF|Sz$D{wA zxJ+$nfPe=`i)(|u$7neIGu^T;BV!ws^|nDPV%s-)kU5=kJZh7dQ=7NE{TY4L)&>$F z=$~E=r7G5Y#Xh~oE~uoqQe9ImqJ~cEKdl`$#)u@mg$PkvQPvC7Tz~_0@9KI!y>Zgy ziw>Ero}M24z^{{@p`?Z$RZbWv*YM;LjC2(0=%xn`{z>)D#=(|ng-tD#PE8|{2-g21 zMT!z3N*pgKU#vq84R&{$F|pAnVzQ55tZIi1De-_L;Efh)mN#K+7~HOcu6V9q$vXSC zk|Pz{pwe~_DvgmGXK}j(%B(y*X#$_+jBBSgnd@75LHc?YXDJqwq+U=u2gU3tvhH4|KO;^Pw zhty-+wx|5Nq5AKiqlS`!mi#YmSY%}R*68#vM1k8B>KrwNv-BJxi#*vFAQzO3??hUP zaA~%{xo)}S+fcg!^RcYx+V5a0s>}4X^D6G#`YgMfkvo#+E(E^#8FCoj9iE@G~Cn4+VzsIX{A;( zdIs1rGWKyiwi^PhRnT11k*Hj2azo8~{2b4xEF*u)IGH}YNZiTXaQK`PqrKZ zlXLFx2oh+g7Q&n-NocAOr!1}i+vjoGwhY@=Hj6i#<4>oIs4VSft1Eu&$E}|4g9#rv z#kimnnyyelF_i^%-jFa2@)o$rQA5l2GafxH>=G*_m%f7YH9dJDRO|VPRuTby{@avL zwHB*Eu?3J)Qixa0e*Ce^Hs^P)0sgUGmv7n}7NkHLu1ho*TY-pj%#;NxXo_rU#Z&{~W?X%1fq!7eShXtm1CK-3V&8K;`f=h1HnA z6|;0iZs)MyD}Q!&Sh8W369F>lmw$sVctm8x>7DEN@B8)K-$1UpFs$P{LP^TWnO&H& zXm+|G6co!It~UQOAM8cR_Os^OA9^s78}3;sVIVhWEgu2%zi{2Px9ri-iL5D4od(}& zeI9B~$i`Jr`mlSjUH=!@3OG(oB`g>RirkDyp?M)PUdGh%X*%)w|7}-1DNtlREGc6; z-Ph`UwGE}6k%7tSLh9%7WPL_MZF=}Yaz?MIASOp*7mMhhN=acURTqu2@rfCcrpD%W0g652kXSQ->bzST?OScK(00)ot+xXOWMQ;OB zOC^6yWXOGd2&;F!fN_zCCuXzxZ%3K;?EnmPd^)1nGdLV5{Sdj9wf?1XLAH-a;=|`# zYB!(I0r`;t*u8H7wzQoERwn|VHU`C{=KU2X>NhV->gtdov-q#$aVIvVdM9j`&b}M% zpgM?47&opU_w`&pPOmIUis;e##^L~^$X0&-J+~^Ztc;#ZLd(xDPzn}QlJ{xK`O+JD zsNUR9(&yo6V-wQ{p7Rcs*qmQkz!!H=^xEa2`IM=$t2z7UIC+Qp!qpobqz%60NU2kYkdQ>THLq zS-2-w-Ohy7*k11CEkU1?pGJ4?HfpBJq(FJdvkr6&9w>r$_s5C4; zx0ISZ@tc~O5;~-s+O#Zr1JG7f7ZU-=!#7cvz) z6P_^394H0Wtrp1UFk)G3a*3WJK(V+XGqx8(ju^4DNFNO7Cwt<}e&DWHm$%U+mgh_F zgcO@`{3A))`6uenF6|fn85lO-2dg?7Pf$TFTJI}L=DUHn|HxUsMlo2^6u*#zq_+aL zYy$snApf0!#`wBpq@&*E91G95X0iWw6n&yR04NRfx(&EMc!=Y@Tfg{OIZ@}5PKGj) z65uwZmFzk6WvrhAGsn69eEs{GdFq0bV{Y-W5Fgkji-z`Wsy+j!h?YEa1A8a9)loD3 z&kRlMT)gUweqI*tnE+?*&tX*{wmxVC+;g+klB78@6&yVeozT~9I+L&186;q8$p-Lb zC{mXN>y8V1L84zqc}5EVZNV19`#d1OU$68FHWz{O80Q`a0zc|6+$V^#41fg75pCbC zJpH9`@dbz|hekp3FsqH)p&g8e^FPWZ#XUdI3b2g#xT2=(6_#KQkb z{NIY1&=gLYUdlk1>?Dq|W3;55TSsg(?7NP=*s7h4{vlN^l@`U@;~8Fpi3rMf=tp)BrFsn z*37IGFw(?J84H;_>_5HVx%Rm7;B8sIcsPH1zV*@;10qc@O4f+0CsL8(GgMw?W3O=4-zcDp5h^Ft*HjT zTn1vThq4`_^_Lj_H%*~%NQI3Z;}47Rj7R9b4B;R(&?`1LsD}~}mWpJK%kDHxd)L|$ z*xOT?Oa}|`Kdw*%>SS$`4QA=amq=yC0b@hqy7VKuVh>xpuVQKb&>IFeG zz!e3=>JDw4pKEF#uGY?gYmq)xPC5xPOe&Wes%;7rGbehMn9(w4mo1E`4C#^;SW)OF zP(L$K?dJRujptvsx{&+5)_L6O``_P(D@+jvrDW>_AW>MY+D(NO@iD>wkqjfuE(iC? z@+X%FwCOCvf^al?B{{m_vEvG^Uo(3o<46yZzmCKd-SaV)&N|+lH5H3>|@6N$m`^I{1EOe zB?s5aRVKQsdRB}|a90i%3I;QUzzBlumJErPX$DaaQ{qHv$U;TR1sVTCXrP7nPgirc zBSKZ-fVfNRMP$BVQ{cLluu{^kiz@t^Z>^^MzjjqRGhD&o#)?cc@NR=4BPN3$SBg_$ z7AteoODXd$2WSlQ<}5AjfT@h1!wfkF*uc2d4uFD#9uiu+Ggj z#Rw1o(~yNmle;4pGS31)Bq;HXXFSh15H$uMJnX3kqUKKyQ4aP!Dsa5gke$k;s7O&$ zU%`okb7CjS$3Tr=fu4bkVW_tx=f^C>-AGzbb2WA#I7d7GOL);LjR?ko$<6OmGv+C+ zp)rL{2}s538yd~(?$Bi{_44mFyCLXaWl%i>M^r4D%w+_b07MJk`k<1HCG;-(gtS`* z7$cB?o2PeYHk-s-H$gv ze?Xl|JfAxg%qDRM5_tP?j`M6B9w(1oTP!+-cn)E?zWzm|AVQVs!gX;;6%jg6*-LaI zO_0f$r2uzZ)Awkvl9LnFtP&NI*r{EfYP0o?-ZLsV<+iIysUNk%PR}?-#aNxCmg*{W zktrVxxZzem;~nh4n#GL6mC9#HN_6bARG$hk!$(y3IKVKe%poM;*uOHv|Bv^j~RtWKPm>r@{QYi$Vl* zNH09Mtj&v$=-W+@vl+X1`uFg5`~KJJSjf)fv!t|4Kka2a173PD z^0CVd2U%MEXnD4}#0^=ojxrlRU8Uhm%QXC0IPnZ|ZASluy;mXE9xXJ2kRs*L&qJ+X zyc2d-K_!S{2mS`BZxlRX&IN3c`Ls|PgYZJ{#Ii(QF{1%$5siUCi8&Ih#QkFu$~?5KyHFd$t3AzxgbCQMfO$ ztK3BU^u#M8ii#Pra_k~NeS^ST^EhN{(bg#ocK}g)yY~BZaLV{0lrxf2*J8Dg<(hc? zXJb5KO~Jw|>;Y$qFVU1NAu42SzmLEzoev*TzXqdu_4m+j@9wp(L%T4{F{|H*Blj7z zyWUKdi)XpGr7epqzh+XhWXE`qnX(eFa3# zsrk8xQstctb3Z8Z=p-uGx>_%w<|6;IQfr{O0R;Rx$G|VS>nQgacfBN8HzeGkBnu?_PzR`#?(5n!hY&+9RZ4{}t-;?1Mp1IwxSWQSS8 zg!8ZT&|E$#k?`*^4KBH4oxpY!G+=C1HnV@q`5MIS78NR{qLi%oVWVRQ06n+F`jv4@ z+d~k;eHxyKXD)~e8%ivQAeN&>1QUrG(?}$!f_{>bA%%;(SmdV~f~3iP7FJA_uJ8(u zZ@YlW?!({z$Z%L>Zg5a^#nykQHzo)WOx;b5hqbRV7^!c;d0Ghurq8tgzv;7^>Jc7i zg7iq^9@Z|Ff(QYhhV)K?keAffJ$5HsFghPFfv)`IPcJ_<0X>59sIm5FV0E3Mk1uB% z(tJT~aX(DazVO`^-z`-{Y(@Di*i5((MHUX9nNfy;YA@`~-$kfoDBso5P#7#ffgxI< z0D)41!Q5gIo~SpV%&W>nLJK6!^FCx}g_MPnjSLh+*HorB(i=i9)Nt4-~pZ@Hsl;raX`04iB5bo*>XG0d7T zxQq#!xHamA!$eO1@jy8is|S1~uzx^qYHcpKoN|C{FiVN!*ha5yZefwdV>r(3xiFa} z7=~eci3r6U=HYU^Keso?OHe1G@RU;}1~9a1Iq9t5;c*SwsN?*`iS_>KUe}Z$^E+^d z1n8gd8n6=>sPy=`7=GX_siJK}6;Yb0(~P zhzgPEDwOK037-cWEgH_SV(aOD-1oB6<0P!?SJ)nh3i;^iwHxpJm$ppYnfEWJ#M~gqUV{owS!V&}uDD(-Y%PV<)D+Qp zUFVOsz0F~KmOSRBMsYtD#5;ru;YsE;mt7L?^amnPoC=Hh$i9N%WBE2yyu)ko#AR)l zB)+iGRA$zwU<-*??eR9wx)|qIaK%17YcN@1LnP^0q-}Fs96`lzrY7vPPn2M$NJ@%r5U8rX-kqHc{a?Q?y?Z4^(k3+C7V@@uX*YUJ5v4uw-;2G`&xxM?ReG7T@ zy<#~Q(BKLAt=8)J*Gg_!)`@S2g!0P>HRVJU(9&JB!w~_%1DPeS7x1|0!jRuDujA%yPBPI&g46PuX1h1u8t`gX z82bGs_G*|x1Msx%g=utYRYjI?yDNK)#e4!hLC{0Ck*NWbFn73z=b9FIm5=WPw1)uX zM@DXLR!0v1zd|ddw*U3=snCQS(GTx zi`L(J-tNrE6kJsJZXK`ellD~*gjDXtk9r))L)W%%A5}&>bgpMLFmW8F)XU@09_IFR z1BeF!UWf5!0meM)*Ndi*9sy_9&aKWr@nh(*+xEs{qnW?!xnbk=ghHnhcm9;mJ;XOKXR?|k}nCiTUt*VnZr@b%ED??uHOrNDt(Wi`^}u<)xclnRuNtZnhKTZY56|~qpAEJ6xA9pja*JEa zmc8{R9DX+e*_H@WfQZM{>!*O1J3fJ$E5GlX^JNyFmn2Lu-C1yL=~)G3OHhkWV`EhN ze2(_2w(b1Fg7&$YZIxKX!ow;3P9$6)O(`J^SiJ}#5z#BR^WntW{^JlSkt7T#%eT10 z^+i?|_Y1zmG~{6x^K9oBF-3Pl$6Ym0dw+;#={Z#_*A3awS1$r}@BGvFfmSSI5`k_v z{ET$GQQTiB-EG%4PAJ-~6O~5KqwDZljDbnunJ8~BTJ+B(Z?A3~8XxG{K6_x=*2*wM zMc7V>tH@qFy3jJ*>2?TSSCmb1oM$_4rxQeq7!XD|r6OcSjAyjItX|kb3Gi;9_!ckG zwySQl???PRO59HgD7k}w-; z+UDt2_|&^}o<*2cnmJJ;TYIe;T3#4shuzSpaJ)RH*oK5@wjLJ71r(}^gd5B1pu$Pa z_h9gB*4tXGzdz4=g17AMTRGsqMDu|j&~2r!f+!E4?{2?;@(%Tky#`Ls&!yyajryDC1u-4H9QC!MI>m8nqbs}zH zx7+i;r#ka>YoPW03}UJILYTqlZLB5C2 z;dml;qV!<{XrdSjGqdcX524bxOr=Qy%R26|Z);!N+Jdz4vFmK%M+pS+ZtE}S=d z20++-k`+e(!Gn>`mS`}4+W@aHYy&2^tf}Embk7c1~vbAuyTW zz~Fz}g8Y*CLpZ>10*(d*Ff2h+^UwTAW3eUvUE9H9hQa4}Q-}pknf*piyC)dV+TT|- zMlu>K4W=!)xUEo&xxd#bnIqR!otCuPG3kmeXWef*-OvBHoyk!LV~f0($%*OirhRtY zJsU2+tNV7Piz)C;2;AwvZ5s%XEw1*B2)iiVXek4wSwo(Fpf0bmu+{qGnSDt`S!`1j zX7TbM1e&0P_MCJAA3v8~;9EIwH1{!Sed+#Ro{NLsZyb(h{jSA7l^R(H#Pxc9W5kf@ z-eJXpEz{lIEO!`O*{ra=z0O=Z9Fzs6dfmz#jmwAzBPs9F4onj^LAn9D3t$lpP}$Zk zTNr{l7YdE9V%sm5y$H>z5kGfq+CbMQIsz|!T@n8uS8p9v)fcUSf*=S;NH@}`bRHUM zkdW@~ICOV+cXxv{hwhdVknZl1?zi=K?|ARNe;9jk_Fil46|=r?&aLu#emQS%hT(Eo zL0J7~cwl{1r@e=qLpJy^%lH&E8;%(-b89D&|HZ#eBiLsGS-2aXd;*j{=*=D79Ih`- z8NrpFTg#V;_V}G}&=wI9kug$vw&H`JL(^dzzTM@YG~zRbO9YUUCFVE;Y&kdpA!7G# z&mwMjbCmBBtdIUq1B5=i8Q(i-Q4^wIpaf4k5^2pfAJ>kcl&{q0kv+V}{WU&GjOUlA z6u`!?BA*7fw8jsh!{Muf6Q#rYDlQ>A5GcZE8W;1c=X)()UIFvFO4!;dm`8qZ%=v0C z{ImP@@iAi`Q<+&`b6IUI%NClThku?KJU_w>kwa0rS0Y^$CH&{f82%;rq?jzo>npwQkn!IMATe?}IvMf+5IS2nK?w*E z5hXI)ld=X)j2q7uTad6c4c$b|UMVDBgciKoDKFH0c7aw=c33|YV2avOIY8ws_%=7`&|cSzA?ToIZUX-?0L1!}?-mm?L3Mb5W6lj*@ExgeYupDD(kNxg z%T`3tpIg;;qyZt+3w9bI`SO;6x?9f!BoG5H;7-A?H&Ga^(>%UTmVaO3 z9qA_JX{bF7W?r?6?DFERd;8S^oyPlcgS8j7(VBtA+xXvM^PCXbMolv;f2RppJ2Tn*9ZExw#clqf0$ERkJ!%Vh4058ogq>t$ zu*9AO*3(e#^=#d^pSQPa48J-(06?kOPDh+<1`Yhi`9RPVTa@4zNlPOWjTicNq_9RJ zBAG;fAW=tw(jXZ(V`p~jmC>uv`1}1jqpQQ!)=!_DED1(x@zD(lmTCleosIh_}^bF3ruj43R341p? z2(`pfbvJWJBiMBGl5VD1FWWZNA!75mfs zJq2~mpphLaLX@rB$1U_uG%=V$y-x6_8MMD^L4Dd=FA9nn+#*%Q+J3sEFDR;jo3^Cs zWi*heGtx*wMsTpa0$N`%7caR@G8gXAlSCZpnkuO*88~s$!xe4Q2|^Q#7@%mTi~}J7 zOLl>ADst2vh*S6( zshg&SEuDy>5)=$P2EMa}jn#S+h$a{kg>x&9tTlJx<{8X`z?j537{v*RoVHv+P2M$^ zBxD^E0TCuq2s&`eX93*`I#e`8fyBr@uuAv(3T460r|@vle}k`4ve2qj9g6%m-K^)ZR%?{^|a_U5$r%vwH67P@B|v#9KxA%%<#`e^UCID&+`b0 zhcYtmWIGa6fDh9l%a10S_6KY*U@I}{KI z&ADgpd0y6!DlBg}*-e%umTgu;R5g@2U)|T$_`IbpkWwwB-@5FdE{~lcH!)US{bGMF+19Raam%-J72in`0<{ie7%@vS10e zJ&G=hjW3Xc=)Lv!c?eM{e+zrv~GOwF0Qbm zkf3qJ0r46Ch3pEKDOE>HvzH!RO4PQd@b`!dnJmHF)XMefOAc zPcv^;5lI6;5LP;|IaQYBtj3s115vt%o$d2Fo#lZcIdX)zLdaiCJ?znSEm;zFCF;J} z=azkr+G0(#Nx)9FyGQlzYNLJ`%UYpqVntqI+(hr(+NZtm#BnimWD=s5-GSeeF$hr_ zXhmAv&W#S!-@|Qfy0v~adb!cf(2vnjX?IKr@8Cz~x0zg?bP;5;d}@h{!7FI0fSuT% zZh9>?iED62wytd0PscO5=t0KyQL2w>bQU{UBKOr*quh=JOQsPNB9lmPu_XjAM`W`w z%}zKM+Yc;6-WJDncz(E@VA!3~sN6hr&iJ-jdwVwsU#Myvvso*62$$XGNPXL5&IVEc z+3Di%N-|^HPq(&L$+=LWs?*c53Gyldg!<%8pCge8FQKS{k|fed)1NYnQrc1~{LleO z7JQZI>NLu+F)HX5t<$1J7D7P!mE}*;EU_Qwv{WQbZK5kGQC0*4lF2-wt^!ju+nsVQ zn+~aS5=!SHfQS1g7M`e#&%S{bm`Oie(5pmEtuiN}bx)|ZUknie>ZmRK6ovw9Y_~|g#<;JgaOk}ZlQYea;m>(tT zN~_9yZ3*8C8O#m~Ei_SLjFqmZdear)(s=Ev@!(_+4tusKo~ia4T&l<;`h+8}$Zq*< z)rBNjuV1Jj;wJ4GZA^isKzrO$EOVD@9JB11?mY<@oZj;MJObBIY&_Qwl8EGtx_#oc z*UY%gvCXar!ao}?mcvPE+26(VMX(s#C~F(@tiGKTN%`UN)4q8HZRS@Cs&Ub+L6S#{ zZ=VsV@rx<0`qpJSQi&0)*;r-yQZsU6}!FKVv=!lCxxqK!QDxG?)EbkxF@@#JXW)l8xyO8?s=<@7x@{sO$Udz|HPwKSe4uoD$VVuc@@1qw_ z9g}=Ou3j7DzE@R1Z~fVM#qF(%%<1@E_}ccuH5*8jZmAqet;vDlFJ;_A62aTefNmEk zvSNRP+H_IxRiWOJa_bZrdK}uIFlkN3-}=YW4XbJ=9_+5FqMrkM>AKDjOpy^QfAfaB zCg;K+ymkBdH-e+hKWlPUnGQ$a<)pT{QtK8R7<@k!?^WN7-QFb-7QMfL{$>5s!L2rN zjrX3@{YsTviCg*4QE}sx{euIXo4O@7lujIpxz_tkP!OI%a4v(+_}~sWng=^QxD5fC zz5Nwy2Rk54`Lgej#>;tW=>s5RQK%~8VT1s=Kj;bUAp)`FTs_0Z&_sJyl2WIPc?0Qb z#vV9r*)N@$sQ&rJHY3(vU3t!Ydc!||v_b4OdRgeHvfQX}B7WxFRuk5$3ElkKxVj?P zW_ZTO?OsWa7$iZA^?cy`z55!E!?!wmTxW1}GTw2BJ$?u__>E~dw#;(CYxi;WL7kbo z6?&-}Gm$sF0}jq^549+!;OPD1aACshAG|O&RBjb7LFCfqEC1B(`D+)M?A|O#+4F%v zL>ase6eo}LAqLsOQhB59=R#hOo?S<8MuW1M=uIUB-6M!4#>?+ zQbK%9G6plWChX^`W?`V++(xkWEVSiZFbuDvA;dBzK9j;t%vlW@AwoqnmKchE}0Xny|o zJ)2{XPwAYVL7hP{3TNtfm0-QJl%jzcz0zvMer51SUoQ#0!=v|%WR`2^2$$MgO|qR~ z=!LrJD4d>h78aAz@g)aFs?6J85MTOWby2+^pLK4K=TFe&l$IfpXyD!9-J&(EaJ^C& zUi|1ME~mTf&QG$OXrnnvIwQ#i>OQSX!(yZ)!2tFnY)etyv%?JL#!*h{tFiQtzI>%1J< zD0cGXP<`+sH=DqSop%8u=~$KR2XF&7GCf9T+tiJ`FtGb`Ysg1wjS9mM_45t1%JR2x z_7f!ZISkmQ*!MbK{q)v4armnoq$r`g14}{Im_po17t>-Yf~yiz;@}$6sg}ZqtG=&q z<>NbN2B;8LKQiz?^N@IC2^rh+rMLOt8SzVG09Iptc2lBxfFKufH2hM{LFRP&RRrs2 z$uGleNvQQ;^qdpcDibICIGgmoZ3?&RVJen#0)km^US4g64IyOZG@kp-YaFV}W-8^= z`Do46H@Q-VFZz=jUwoy|*-SM>b1a?la*25lSei&Y8shH1wAh~7rY2b3MZ8U9ym{jB z@Q~?65|zVb?SIoIEXamwO}F05fh~^!>wj&yedU+<#q}oKDud;B@1MD~^)ET_zbxj_ zRBO^qa4NT_YIsXlAKrR&%G^4vGudp!0VItO;Xg&?mkE8-1L)H`6 zc@KwF8#(V({^?fqZOa4w(QFq*CGkfS~B=)dqsWUhMuv7{fr z);&{%cAmBvPi2gK$!;IPh<--(ssGxGm-)n|)AoW${j@FV<@C_{baCRW_6U9FenXo6 zW~zm9{e&fUNw#XMqj7C#JX+tg05yA9w#dl8vhnfpqcHhccK5IDOrm~YSA|xV2hhT~ zFra@uXri`2Z{NPvO@rruM6+DUlGU^Q)b4((_j~@ssP~1)c;~s`><`W8ygsU7qC<+V z0lB?`Z3_7o%I_K);d7Z?Ns;LXNojp4TVf4I76Nhr6T6^*@_S1$K8B5J{E*bOoR*qI zALDnW#NSq>0m$!ykGfeU#3cnAdK80bS!EF=wBvQPh{}p$)3RigTsf1BHtbptl2QfD z0OOY0*D%}fn_~$AH=xzWTBcx)GkE-9yFyPlojW~(h?70C?dz1De4(_*iXs5_XdMtvApyi3`Y3Beh$Qn;(ublXuopf7q#p!JM}OU}}_%pA5T27sqjI zJRMA7{O&aTtrwOpVqdDIif4e--oMVvWB5|Y<$2piLNL*)>J_wFD&EQRL&w9-(O{16 z^H!a^E_JAA?p}!PGTVWnqEbJrw0T*12*h3@^6%3Drs++Qpe+yu73|$pI0LkJ7B5c! z$>xih0m7Z9rq>Ex@Uo%NBsP@dn25RZYta^U1l~7&J7zVB_C@L&a-S{Ge)mUUoy=S; zN8Z)_8%}ZP(YWu}OvlA%D1ZRvEk2jY>Iny>M?PAd-RN*!cY}BlvIRnklDYx(8sKS= z_5D!ng`Sd+yE}Mog{&-QFmE(rN54f!4^_QkQY!oF_P!&HqP6`p^9yrc?)&KDH|$1> zRyw?-^pRx+<36xm&cD|{vn8;VId@NGw0rmLHs~E(b6Of|SV#?&F8ud%NP5#O=vG`d zPprk#?aO~{dR=HJ2?0ace`C31JiYs);?WIe_){qbHT7Wo#GhJbC!FKWW&3fx$4bAZ zYXuFBZ(wOQ@}NCthoO&(N|72mf`rI`RtBDTX?CS{aC#@`VUhMloI-yc*u0v6X&6-& z_+1G#?C|vX{HD(QTHO-UIKzP%s7)sHK>PV~Pn78X%$M}O-}Oy5Scrl1H3snI%2h_k z2>Dx!$!KCNC>l{R?>=}F%vSX*87P_rllC>{1xt~Ah!4^GFf0AOm#l(gxkqdkJ(IF< z=l5(d9zXp}?#w~S^$nOHc%HuY;&l4#l8J{S8B3=^UseK-x0ZQpF&j;c`M-z=io#?T zqTAI_&%ydyZq!zvmo>M8>YAFG8fLPhwiHkl=z+bCe6wy0yC@id3Ipc44hTh z=##@^@}0$j_b)9NQ2V=vnqfgO^F%zGmfSK5rJ`cjCr~u%!B>=hv67;H^;Jl30gYsZ zW?p1|$65O$N}*9<3C$xR2RQud%3++x0wqYV+VH|%DuvPa$_kGTf>92(Dw*9cnY1e2 zxJH}|l!*Mhh8J}6QU0DW+e&keyPbDDOAv6r`z>veR93{wxmHyf&GB+`{1^d;-2hHdJ@%^=Z7w%Otq^iKh0O}{KiA zvV?@{XRVV-W9cr}iEn|3RY2WtM&tMepR>h`(j8o z&h;Wt6HbiA%MP&8(Ymi8cutxo+%xdWu6aGu~_3yO_BZawGQ2zEvv#qO6CCB2{#X>tMiW|JEPRA5Xq#vpVk;43d;9j@h z(E8U1u2T?M>^}4o3mpcY9roV^w^S3GWiJB6Sl@>U+iE(3^2F_#ZfN&4djK6mTJ!Ml z@O)gqXi{x3#{%jbj&j`_mRXpiEdDSOsR4gU;vu21g+Q_3lUlnFV;N!ne(R4+K7kmu z6_b%6xEk^S*1ubMgwJy1jaWw}w${=r^7ri{oWJiF1Dcn=_Cq?`yZ)`zszFEHtg0dyD95x-dn)DzME~Xpg~@cnE;OT3^5od&by z1=EL2d|toZi}?Lp2PalRLZS@zsBZRjA05D$G19mqVf1-FIrrIwsO`&3Qs&XbRDP;Wn`TUa_CyyOg|EA3~c_V13RhvLGSWVkAapMYx1g3xMr zAZTx#7~G>=t-)q)$0Mm-N^9JSqt>zfNB7o$9$&kEtoif5qMkNg(mqAXivDrkZ+s=c z4a>HZjOuJKzA>4p)>|Qdh#;2S+z6%?ZnYsa$u4NxiJv!NL(HBSEKK$a-rmOS*>E$%p<+vSs*>*Rl*J^I>4H+Ud2O4xTV&qN^ z{aC~f`p;ZhNoAU?g=Fn@_hx5iRk#5WlREHs^(r?9*JaF8`t9pEZ6E-3;nMEHOPp5} zYL-S576wjl(!5x>GCf}>EhsF!*h$ujo8b-L5{)i`&_jN&AW&!cfR_}Nm6-H;K=N@4 z2nTK>$ecA2QGyOeKqP#!f1%s%W9;!LE8+;9W{0L#vlBX!IXV+k^2N}Tbb-6%f+D8g zP<`tA60N}{3(1cLm~%QD$;8B&*VcT4m)TfvVmP0K={rriJ|O}$53ZW*F}R%sWB&ZN zBX@Hn>Ef~##tL4%9mfcoj@PAzqG7I&sU~VkB(Uh5_pQk+2H!()UjNWSy}IioH0p4N zp3q&LFsKi-B`y9Yv-G$%%en_Sy~LR9*-nxJCZy6Ce4w8%U}#;ypd+)uTr=AZ>BcGb^3h2$48C zz3eazNN_?nw9DB){0rRF#Ov1kom`X1TPJPIO&w$dn zVo=4Np5O$B1nzU8yGI++esR4!br&B#oe0qr!gYPZ96vrQ_IgxFNW@`&5L2<$dcd}N znb!X~IJUd-SgUF?vBmSeVQNL#6tu@5ZnsBrX4q67!oU0nL~=$rXlcFl;4CMr=1K}< ztTo>Dg`DTSe9z_e=t7FYJE>5K-1Ta5dQydMeb>8Pbd{HX`;b6p52w>R-JU)*#AL9Q z?#8pIwUC|KCheYU!h>+TNM~)}{QPs}eeZ||mpS8>`I`Q!-y5rk>j}K{?>%c?H<+*A z#F2_E&k40%yN*0eH40?vE_{CIFx3-Kc$z0;1+hj*jVz zHiVBkN}mJ?k8Yv>xC^h|IzpBwyJ0**Sn6>MeC7nE1^KPW5IT7Ag*(&y1T^&7n1 zj6<;FS-fyU_9<@qWZx{v3Q)KcTPqia7Ae1}wiSo4bvcZ2iHD)(LzS7oG2pb;MEN?h zF=a;l(o`3K^k5=1X0m*3l8I*!Z2Nlls0R}S1fxSKir~aYO`ENMPzaZE{x26Gko~pZ zo!{!DV;qws z#bKj-c?aH;3j%N-VAcs2haK5DeE2dnXoPwa&kCa|lVG&-N{0KxRDl~}P*ZeMYC*c>0>vkTTcv5%%8%(*gMAwRggHxzDN#VvTxnek1Q zZa0k{CL$_~CEol_SeWuUB*(|!H_JNshARC8;jWXdT}9?*6F_HL;kD0JtwI(z-<$|i zqsGVAi^1l_!fOJN&8_`TwA4Ru9%KQqV0n8swJ~ane0mHLon`#VS1!z^Q^1BuP%e%6lJ;@nRN>*^vX`{bO z^J%pH@P#{nqbmkA3HW83#Zy;1t+<7S-R2x^jH8rl9k`|BZSq!VHD&=np^3-*^qy?! zceK;(tF*p$-TFAu=*%q`@=QtMOw5tUGrUIZ9@3Z|b+cQflO54%$aHsGlsD#W)B=^Q zk$bj(asKN~Wv3Bz%)k&KD#^M9UV5}msR;Sm>cWqw<{*vJ)bOyw-`Fr@!S&?CiKin zP)lJLcDd6@HXHf1h+027vUc(mqT(7`&td?ZJb;HgV@>*Kb zU?s{W=k3cKm7~pBSvxysJiG%+>zPV;=)w$#Sk zW!J7f6T_DXJ_G~QMVWTB0lyvsl)I+@E0u4Vb)a3Kex8t(_A04Xo!!jmHeOMpDYi7k z6?9DWck);f>o-86<)6CN#U9%nu`tQ6g~*6k($Oo8mg2~L%0C(NVop%yvHS|^DKM8P zaBE9ARt%{=CCqRFe=!voQ=~^G`xie~MiH4F_d4M(zI{KPYoZ_UrwlC$`y z-INb&cuuZ7j?%#fx6Rs`C5tKON=hjd_K2wgDy;0g@>veeFBXyh%JOa-R|%Obx$kBe ziG;Zd`?()uL%#MgzB?j)PcEg3`eD|wX9I0coBgqRk?zaqya^xZV2828osr?=>D%fD z3_(Ko%@%vzAZJF;=dFc82J4Ial?Qw`_yNNDOjtU9($M0X=9kRptZ$CDMgqF(!Cnn? zeKmcxxoxYtlE^tF&>;d!fq`cIRA+3mF3w8F?K(tZ&zbgw^Hy-*eGl!B2IanE;uP(m zMRBRJ2jt64J3R@U0u!QF-PB(h@zP0*-Zap8!XgHSCdVJf69-S0Qz1c0I1hp#6Dk=p4n+@!Fn- zCgSX-*B@f z)rhVuGJ`QMlYoGy(zqJ6Yb5osQ{eh=Vb)Kcv2yt}j)!*>zxAIZj~2Pv5oJ$4W}8d% znUAifw2D-sI47sN?a&sA%Axfz=Y$3s$=7A3uW1nI5iq%h^P4KSma+ZgjhUEE@F8%? z)fJnu<0vLNvCP`88*Eu_-Xk2dmC?1EKLXooJO1QItHzoxcf3R6csEiBs$z2GRaLSQ_!?`c3N7*kcEO7$?Tzd^P%FBU7Gol@wyN9OD5A`X}?f9Kn&t77*K6fU+w3zKD)}mGt231LkEnAW99hlCE=sZFf z1HJ!HLsSN{C_=+}^~)w`)Kg{s|K{t2{4^e>v$VnMfm%jPPLc123+zZu%I}kwXVI)o zV}5S3T}DP-Lk39IO20yQ_}#+j@v}bT{|{N4WpvrC3B-iypWq3<-Be)W*A~23KhWf( zM>4;3eG0@flu8?3ZgVJE86cPS9{W8qo+sO{PMqEuCl945mEwwzLzpd6-~kieDc@Di z!+?}*Ww3meD_|L}2S5Mk06j}btJe%?+*mWHf|cy=qD^LTS=#9;Ji;GPQ!~Epms3-| zXM~EMs#@W2wOW;d?<0g8kWL>vsHDE%OAf!cyfTs+w~T;(w|m*;%~wF>&dPtkL1V7c zKGwPBmRbEs2*7M<4BFbg)x#LLu``h?m0YV@YCe4Q;pgLHmKBx$4gI6z--?|cjUn(_ zqqB4NXHSWDmqm7S2ST47uIOIc@1*mxE;$(*l1^pvt-BInvb?hm2my{(aK26t#dl!dJ#dSs^z9y^Q8h1@q07F| zjq~hTK6u}wU+O%^)u<4OZMmF|GbAYpedc)9rQ&qr&6?h;Hd(=FyKp7N(M}a&w-_Sk z${>EBS)oGRIM{QNJB%pwau?bV(_>0BCvIiO7FxATxDNOIu-v3EP0P`7{8CVtihTc85gf|ASIeUIdL`H~&Y){T zDwRDM3AR3niAQjB#08uZ^Y?g3h~JbfXW-EHlQD}r`21$7%Y`JAXysP5)FnVO)JN)n zAy~n_uY-x^grfX_n3bDmO;YCV1IEbm>Eyw4Db&4mU^P3*Y?A;KIbz5D=Ze7k&98i% zfFpC??&Zs1r2X)>X(BJlIyECp$rL8e$8I9sHDDoZVyzd{=~MO%1+wXE4c{7w_a8$@b(NHskT)86)$@ z(*L5M?ng07Y-GwY%my$?7}YQzM~ZQ5 zv#peWjr{~ALFOxOXWaL;wfBE#GlZVz^azwpprNXMji%R%@55k`Z46!+k&=02;`Pj5 z`Yp=vijbkJGyc%>R{EskhkI<*k+y;6Vd52%RK&Mqfc-&H4sbvKdYPj+6^Aw`Vo7H? zWUg@I{{kL!h5rE_LIr6Ub$Z=m))(f7BAU!J0zY%U!p@6ix2@`?72KS_V>0bF5h{-K za5!+yNmuU1qOKY31lIueVb)lGGx2fe=XERfUalLR(a2J<5``Aq%fQ~d`*0m5?-i(y z^knqpTL3r#v`xAEe08Onuuicsmz0)~;fI1JifIr#1bhm%4gIdK_W{mSCrOpDLgA9j zGr}Ef@M0Ri4WWE%Av*gfstWfB{G~XaOHF)ss*M4;^Rt~=y7r1e=sJJ zB8|lh#?4Wvssxvr6%-XKC}~F-sWq)w$>S3*K_Vy4sF^rZ7Z1%NY?@ZqP$z4Ce)%#y zHo{$C!S^C%{2HF0T^SrAN-j(er$;W;_YS~GnBEhS6Ydr_YI>=jBw=AqnsoU%MJd0V zOzZ<}Qn|Zw?091+B>$BSrT0>x`ND-1{#bN|bbG}nN+L>_WY;qRMDWEMX=P#Nm?CEC#PsHA3CT(9S_Gk@pBU)+ ztx6UkguyL~>HxHm{~2VA>==YaZ~-~-+zb`Yf8;?q-~v8JF`Kj>_a@8E?fP_SLGRtg zj{rfmP*dEXuu372)N%1?&e5NIve zAOY5>dflzD=HpAaxp94pZ2C{bKZYKK*vGZ1luuB%xIs$SgjFVHAI%K7|0z>J!b*TM zXVMZJvx6rgVR+&-QP})2J;>c5{an=KHaWo&n7L8-$eUbZG`Ys{V=HO;A1Z;SCBK}U zpB`gJw~&Q#!lU#0{P*{&vkmwG+Hene{DG$xl{-ba-Ve<{vErQ1>f_|b1N_Oxi^T+@?E^Z%_`0TsL$% zKx_N801Oa9KLMNLvM_g?$`Ug#dV{UQu=3951KZMa+{D`!l-2(zdJv5WY&c!@Sv^gS z;kG@TKU*6daeHQsKEUuDzDkGIm6`n|6z3_t6{pVL;qJ*;S%Y_j{`TjV*I*Cl|1jE; zq5fa;8m%aL4?pf`OJ>`1=s>jQl8a4lxJDJ8kPi(B&pp{g#3g8}qm<&K|X zr)9UqOx}09cijpRS|~H-JGP)LzNR$Yg*t;0~@ZpKmmc%J9Zyd|fb0y~V%o zaZkSE7y1j(&@x~pPTD$94C4{sZ7 z)-WjV;Qg`6^tR^E(%$voqY+R;O6E1cI+O*90Ms^gI2{kB+k)fuO0ndc$ENtETZqG| zs|{Z??f8zf$(0q8=17EAI`6&aX=m4ZEI-}$Si>Mw`I95I3d-)wW066HMxT}}o&N7L z`pvvE*{!2a@6n9re7%uDo{rJ&BJN6)lJWqt)6@y>B{6$Q`40PDT6pTMT*?=#<}lI$ zH><}zZd$yR?9v93`a7>D57FDGduu8#MdR!afJ=fJ<4wh{NaZ)|zC84) z^XX8G&Kc=qjmb=n*3Knbz5cTJ86Hnir!7Blcv(M^WAQ_~8B?O2OKr`LK;*0c*mux= z(xYR8p%O|QU5Y`kbZ=u(to{;)iFrg`(1qOq9*ggh+5rvAb_T=9D7 z`|K$a9;11v%GIUpUALk}->YeE>XH5r;jBpE4YD|tIu9Y=^diq!w3_45uw2d*O@P9(zl&EOGDwgWR>uE(nOhkpKM z*ClM+>jaS;v0;@qaM$Q;4`}(^H*w-6{Ou?EyOj)u6s87_xz`t=9&BVz7tHDEl24y~ zNvZ#|v)D07&RqZ%>{|#dru)~Ip%|)GrS9?CCCXI?!QP(PmsV{_%h+qte50VzxsQO5 z-2asT(gU7UD8 zi`ruDzpv|U_!q`3Pn$_~4ZXYbp)TY{$9 z^EZ_PEoa7a>{;3Fn}%8Udqg8;|Qf1no5+-OL{yaav(d*RHtKX?XDiB93qgbI>-!&+pCCDRzfG zmY_qL-%9I$%3ZlqU8HLDB7F$6tg`kh=y=bs}7P_l-Tmi}rSfVAXG@jr^}|8v#7ioXYPtab_RhhHL_M`==gI|w~u`wR1} zQeeqW&gYsvGG$uCXvl5HH>GN#;sZCRX(z@!lmq~6 zfc@YwkL7@&%wtH|YRmc##tb^K&nF0|*oV&>r}Z0(zELnqHkaa9SCj~yF!+yK`1he6 zBOwZXIc;kUC9qU~ca(4!LO-3?f4VgXCMka!^Sn&=bBQ&9++cZQ~nA9-0z`+TRS205l@<(~-&2GQmdSM5*X-Zj(YZ zb|OryFd_DSQi5b8*5sg(!gV{}yiYV?{#!~mv{=)yER)DdV*IRDyv!=>A|P;V)DL?|^zhFdd_VaG@`uMVzh7|0tB>Jtu+fW>LRZsI{);rWi| zYuZPeBM@nYjbg;3Eg6->Qc;?; zw+h|n6A1Y=+~n!MEg}FP0Rqs1c0GkN)s!S5&?Z_p>7?8{n)4SKe>N=aVXs6t3{89r zcdSD)l*ypl2(txx_9PLB-;^@porznieRk!z4Ma*0wXWe@$*S7=OlO+*^N ziTyeOn>gVlGOuJ1mslXDXuTyzs_*;F5!Bp?L%x~g(p^JjL#QY~stl*UbRn6NU!htb zFypumy?i>affbqNBht5)Ju*H-iB`Nypr*8Rx7PDUro{&CnFpSe1O06@0o^!LDEV9p zhRi;|E4Z8W>(&J{*%22&=|T)l^CCtjqa;3k?(6T$g-eb$#YJG&;<+kIpEf>ve?7qc zhGWv+B%kjY&b;@-Q34W$8Q&X1@288aqq%5-kE?hAA;C?uC=qEWMqk4hLnJB86)@OK z!v!P-D7(w$Nt{xqCPwa7YbvUl4P*C}09x9eCSrR$tGOdl)th0flIi~vlZ_l1yq+$3 z>Wt%t;SkVnGNBN{Kc){AReZL>ET1>OZmCzUf!Lk=SX@*uZ`bZ$dcpjGqu#jKl1nrD zlg#R4xTT=5li*N?w6S;A-qTA;46Ha*;;@g$TGd=istNa6>d^LR>e)SUxLvxhMx&m3 zBnFJE<5QZkKAgWIJga#PHjKKGQK$2quw!z|{9c5W-3<49{I( z=Yey-(&!=mD{defS*#Q*l#}Oyp~CG)|2Tss@*lYVd0ew*GSY#I>Q{%emRj``9307Q zZ9G#`Q|LrtcW~-PeG5i&HR(CIdN7uKWk$N6rG#qCokPlE`um$D9Ih5^_VLxMT-?QC zbTzm|g?-CAjOz-k=dwxGLVc~MwCw2=h)`JRelA7xjhAoOH@`+*fD*~@BY|Qwn^_t^ zxQTp%w5DZTGT_B+Ju_wML@3_K6nkw70g}Ljmpz;(dfcKs<0=)K^dKY?KYg~@1*~)Y z+$as{MjaFuFS7we2*Ck+rWW7+0=Z;kPNq>X>v_tuon8ux6R3K+-#*d|GwkXinqlJW)}J z>8`F?c)BJ}A}pzs40KM%t?>J^au<^W`cQvFr8d)Qw4zIzdO}Pf>#?0vFO-li z-Jp75jl*DBWBV;Bv(s{_*+V|Ymp;?GZA(9PX>+#s6DjgCE{wf_vpqaybsBqwHLJ+>(mk_fw8{!cvl+*oj^kBlRFvgKA@)l%CJ=T1IkV7RY%) z5)wZoY#Y9U^yP?HKb10-BA1fy!3>Z(8L6ojg&L(1%52QNhZ$j&OM2_*?qWCzZz)00 zH(}M9L?G6x)_g^${pmdSJ%8X#uxd?F+QOXHxRIx($JHvc_6u_=K~-x&_``mM<{xV( zu@E8+r;up&#rp#y{_R;?PD{1A2$Ae$4jDH^#6zb)Yu_Nc?>l|qPg|n0We$D@MJLxB z$D|`sp}xsk+yC%>$c}^q(xPPnOfeJ7?&!vC;LJ9$aB-MqABlH@)c{xBr--n{zxnSgNh7@2lKYoHU%X zk}w&5e?QkeCR^~aN86kzCJlY8Z**O_FN0ydTX`YD>UJWk0miS$DP&qVTee)~XtEmh z9i6m7Q(R7^>svakZw=K_NckPabwynvv$A(}0;xH)A);7C2t{0f)I%ZO&s^r>dr?PJ zlXR4!dI}7?d5WraPN<^KHg}HBD_!T~L@CU+P*D7L(F^ZSms7*3P++CyVMnxr{eAZiPuohzs|2F`Yne}SP^H+eIHQJ zyW{*hbyQsrR-okEv$E3|tNZWFY-X3`ni&?)i(ky!EV50-IkZ{PC6z%NQZ4=RS&aEw zrco?Z79}56Pl4R~yZR_5QJ;LNd<$Yx(BfFz&NWKM_2vYT zPt+%AS{uFl{QA!bF$^eh$zXB6k|f#H6Q(P~^17iXC(`a*_lMteKxvCgSJLGY|1TE+ zx0h0gTU-Pnmw~VjxtnWl75ft|^DDgPzTyTVm;9K@l}`8)EF7xHt%_LB`BI4Av@D=H zStpyoZc3|@);AkPmA`c<)pyY6Q^{hbmutf0ZBLz(k!iu>8r;cY+b7wZ0T#L5)E-=@ z#bhImJPZlc6a1*Zf%HW_17D_9R7-pVNkbLUT!?DN5qm+KUAx3-d)aBdDY`WeR|dZ@&v__; zX@SzM0c>6ypGu5M{SC+A1T35yvzf=XYJIHcXOrm($Cija!1u%(g%gVqrVwlH=!`6e z^smk>IkT*j!{2ruLSbsPoi7wywM5oNfvU`~uaqor}eI8#wbsv%~wxWEFnY@L+r| zWpR3>!;9By-w|qBimrVViD=1xb@bXAv{pFbA+b|2?LCC!Eze5J6d{FdRv!^^L;hN= zN0R0^y8!>H`G=5I$lC9TnS8B)wb2N9jG%<=c^U`Cir8)W$h`$*tIj;8=!T{0kNj1g z{I>Ewa$cDsmEzyxprcvTS-Y~ZqA%~lnk)EJC1>jDQrVf4Q!kU{+|Eh8W^E~|tR7HL zi6@&54X!}RF#gdvquckP-$Jd@!m29DayP@O44IrqmwS7z;TO~%Xxob-w=YsJ7t~sk zQN3p)U8}4N4p+!+90>a_;Vp(o5H9-qEKj|U*pt>Wa0YQUMnBwgjU(sxsK=A2_X;V- zzf=ttPO?o$3C*1cW6h9$fvASfx_BKsMCP}ST~riTV1hEBoE)&qi*gfsX3Su3rG9MX zI^b)>H{jT?5)y=01`b}I1l)kvZ@m|x1mv?X?g{8GTPpb(AeD1;Oc4cowUsuYvqLI&`P7Gh)}XK>T=zoXC*<}+m7MwI=|vq zFK*ryG+aJt!I~huKcXKrFHw-R7e7!2xPnU0#q<-| zWg-RU^FHf-V8_fpM!9%NCgn1j2YtsPxc`4xd&{W0p{{EbDXztx;!xbRKyi0>m*VcO zMT-=7io3hJLveR2?hZG!&-1?DH^v?J*Zl|QS8gzsm#j4bUi#QW!-ckAb7)DOZii==iC1W$S3*CRWnMLaoQhUpjw6q{ zQ~>mON`D5&NN2WmJYV7umxjOQa7yD!N)TY>>?rC%z1`}or78YQ4O*E}+ME;LeRhXB z=xw%?4 zPB9q9jBGWFbR1A*0h;?+3<7J8K*?H_@9wSj@pv3*eIq0s z`6<{|dOx96uB+uUp7t=xM*)7m`K^c1DG_kQ?OjdPBuE;nHOF^L^V}Ox@7Z%BQD-Pi zn4&Yo0&@W|)nBT~k5_;~#On;}-_Pwyg&?o++5M!)mCV@L^Tgfm-PZvhJ&wQq`<#Pi zZIvKv#`HJu>^M}5e;^6uz7a%|!%Q;w^0akBTw1JuMg48)M=Lw$lG0*gmrh?HfTUM) z_4ERWjsQSfm);LO2C+}bX7`*{OSLwR@6s$>cLx`BCHbS;Rcu{6je!gG6k&Jisuw%^Og#+st6o1KmQ{2m0MiruHTmk3XzAdPKn!B1=C zcN5NKkDHsj2u=%*2uGTZ$aRm(vVR0HUMss_@zsxIxW@iMECmff25srSs-XM)8~NAr zJO3w?IS4)}JsDVlO=*l@-@juz7wMSL9~Np%k&222q+rlYeSzl)VS-V(%__1Rpm*M-03c@n{l|{H*NhP!JOwRbyR4#`I3{2ey^5K)~NG9v&6~YAqaL zniT z;lB&Lyq}Z*OK-^gho%L9QPcs&W;zDW{FqeIB}+l z4*B(qGz<|VXYu%eje9K8O*29G6Aqb2CrI&;=kaO_rl|so1Qo#7CF4wHsle1uRQflF zkGgiWqN+*?-M^JfDDNPuXo9-C4wTyPvaS|#?4QpH`6Q)FH)yoVL=4*XnBGA=>YzN9*6W`iCO z=e58R-dx!yIf2)iv6xXBV3GWr9(>hYRDHlXZPmK3WyIzDcRkqXDsB@`*$;wKoNpwR zu8Bi)!x>sE<_0{O{{JS6KaG2Q$_#C~nxnv(U66qxUZnH4>lO&w8E>z2#wWK|$`3bc zVNNeu4GOS9#KNkc7}tPB;XLD@PM2@Q=Vql~`4~zNcuI<9q+3Bk<;%YbvcsJJXgX5OAdQ-5u9gxE5l9ocBlt9hhgcixcWSGLg&{Ey z5~QF}ifZLov@AEvyYz?qyHdiIguNdDu714$`7lF@^u@o?ZYhOZ;G0fYGti<+QqfEk zfBU+R24yI~iwH?GewZP`q7}1(Rk6I!of-S*u}EFV$MeGc9vd;7e>%RE%14o9!FTzG%Y1c zsOxa!ex&E$WXz!eA{4{b3<%&PWe6$U@Z>jNcKg{_(UlhsoR%A#$$Xt7hH^ds?lXNBR+2Qk>hPz6 z{_maHUVk6OK^=q+W3X~-8XJrt0w;{i)tR7R)*RJyNQ}eEkaN@1oDw{85|w9&+#*Z@ z86)RRw!q@79V_^|0*-Skf-p_UQt?E1RDrEF?d;Dn8W;jv(~GMS z$}8^;?cp77SK#&cpsf*^Y#G?#)uHj0yF(3#lN}Wy ztQOAl;6``&U9$P6S98o~w$L2cTap~6y5qg$z>AO3oU zcuY%!@O1;V5T(MWFi{LIe(CW~-p*dUfMw+_z2rKvS!!RO&O5S1sTJ2>%9$AFZ1%-R zy*~Kvs$&2m4yx+3PWl@6i$AEk^>d3s`X08!9s~L6fW-vO-eH02Rcea}(RaXT>wRD( zr;^s1`7kP_Il)Ez4a*B1HuOF(>u>pbBapEQ3X@jOQgAE?Vd1bv<;5EX9o~WM0l^!0 z58}7^^83;KzjKu>x3Y}YtBUb2Ji&b77H$?Pj=8=8t-n<82l3sLS0OcpdbCG(!bxkh z20bZ)lct|v;?SQGD{_M!PaCaKR!h0k6zTI_kKU?`P6+a)!y?;#Yyjc-GX(vkFEQF9 z7?%&PTqd%AL=aO450^6$#q}k=d4dRe^5!g_G(Mc-Hf9M>h*!LA0Hsr4G+T71MK}rO z2B7i~@{*hNhn~vk5SwC2i|ni84sTDQIr7XZc7k1N>AIjesq+;kcTp$sgzU+5UXLiS zfd+09pX+~wDZq|YZ)psugR*wUr+W9_JvHJsXkvJGWM-{L&*rnEYFVha*Tom96TNCf zDNdaEl@m8A^6MUf3{=xYnquG+F$KY?xEG7#NW9}s+jO7MU~}WU<*H(x<~CU(8}q*P zdIy5lDaFs=O?_2OKo9Q|Gk6Z-ELE%aBMbENh96<7A|@xwL^>D)_i2o!OVVRre0ngs zZE1-`(LoSm6qJvj7BI&^5aveYBa{|Y$3PEKc1*WLhpUjxFKrB!E?L_TLN6IS;Y;0pm|p%-;lB8NOK(oACj_nKw+l{r}732 z>Qj}}3j)c=5Cc3-*8c~pu(u2+SZYZq=Uf@V@5~+6Kx5j~FPrS}(g$ySL{2dcA!N63 z(;x8nMJ3W$F)cs+!hc#Ln=zKK%jIN25=WX0&JJY#$`{Pi$=hXSf6j8{` zXuXnh{72ZJDsJs6#QJYqpU5(t(tiFHhk)gMJ5ItgwT;VI4q#a&wzL@#22bZ{V@-y6 znPwQ{idSglirmO3a^kr@^;l{7?5tw)CjNZ<9F^2^3lNTsd zHrg6SD4izpdP~OBaW0%uVe`djn8uwOEuZRr#b~$0XgR8ToU`usHNUp%V#k1@PukgI zTDqK*UJ6RJn;5N(QtOh8zU`VZGf@WhhYaW9g%X`Qw!2uU;Zg zQ^c!RSES~A{Atbxn((md>ILY=JiGDZ%*t~py3ZrS3=s=eQ(}QpJq4GeV&TPELDW>( z4bt+OiUBnvO*~H_R7yV+4O4vW?G}Ar)6Ba*0Sv*z|0d~y%CIi4x0p&ll~BO6)}*vd ze0|pm@8*FLY%hEEM~H|=dWe_K_$xtp+pip@SEmCq85&z#x76LW;zmituf$OInSAYn zH;xAqL#>E+MA>Zn|9Pq*`eUaUv|=*|phgZVEHCmwN` zQ4U!|ApNh7jWtwZ{M9Z!646HTc_gQ~87J~l{3TzFZ2WH&~A zgJU|l#XNt(#R^*<-(;q4{OKP1kuxemio{_0Kv_*cA!o71iG{aE%T=Sf*l~H259^ti zQlB<^B#-koyTUkkh39COEr^)K0mG>|Rn+%nOSf$&3{SLhZVDSThy93OFF)cekveHM zWuv`>qT8(&wLLj97i0?cSfgKW0lT_S%rZ+!JjPCfM`*uR>>{SKR(j)w)Pa-BGO@R`%;cW4XoLj7+ zFJaeOEuT9IUu=Xy<`2iLfO+de@8F(86_QJ9s)y%j`(vv)@V+u7pZ;ZMA|E@%Nq1i@ zW-7s*Tym)`SAQ!FT;{>Cj>ez7o3tx8z1*#Qjoe-V@`a0oUL6{e#?U-pf^8=vA7)Oh zy*(C}R8!i+f<;eN3sdp>MW zbTggTE9cbuc`S6If4S+8NrmOkcr}1-KVW$CLf?|kFAd?wt~FAI@5%Gl!hV0&uyk== zs_6!~(Bw?3d3U;Vg1j=>C4E;uHsJDHS_s_*ZNz0s+q-G9Gs;NgZE5T&npXB0Yj@yo z8LD2xT5I^(Ht!ds?Wh};yn&?kvZYY2{F2Lw%)ZX5JU^+CK2_W`Jf^vIIw^iEXb%If zi2rU)^LymV+gCqBJeA*j_JQ*3YY$aATkCN|zvjT{S45_$m4nt;w`CAvJGI^(gs(i! zOYJ$CTYG%2DNgB#8jF*FB&sjh7=C8&@Fx(wKN&q;DE5S(z#YF<&ppsfIA7_ZzgqD4 z;%21eT(i8dyM*tXe%3DR=K*~cVgw@B_zh;l@_aL@(`{a>YayyCaPM4xJA}4x)6Q9M z4aud_=cJ&Np3Vf7p-CJx0JU!3>n-)!%EgA@;Y_E|`OM0468dWaqtv(+TTC~KR^EWh(>&6Tq=Y0)>;Qd&TYQSScmXsmy8 zNMaz8qMc+MM=uG|iI-4WnU{D|PLQIZZ!V=b8zfblglsWtbH1>=uCO+Xc<8HgB>miC z)YV_L6T8m~Df#BXR+cb=)?v>tk92n2DZJJCP!W0{mvlyI14f^ctG1Ue*F5rOj944m zBJ{pp&3Do42R&sPlBG92+~Q)*k)iumHc&Y1k#1Lk?$~xMu<))a2^iVy)GYTn4hVCe zl|wVU25%ym;|-;|GeGh=A3KWij(`40+bjQkh(h39m&Aw~%;+lSQ_O~EMpNU_eJkMk z_y-i^^!33I!F2UPlN)I)c+~PNhS7-PqHptD%urd@IhVhpT2VJ37(%B1MW;DxKHsk|s6jVm-XM z-!J*mthCOG3EjmN`Pq}uP=6Y2e}4ez{96xbz=AAPkw3RurJ&r~ej`ja?Ouy~e^+ea zp(SSm1nF(%oXnV+D^9&Vx#hJnLfz#xa8sMR3ocWB<4Bm#CiDh(_yV3Qnn3#~WtDfZ zCXsARt6*Y|T^;++XsfQaP!z3zx*NK)D$|pWM+)B=;dt&{hUm9nfk?ciL=H-#&M!3& zCv=}YD}AMFcRLz;VCL4A`h#PeeZvW-T}^q?UHoEcIc z>4c5nsmS#lkQeIGJl=4&UR(ONs9t{zBaEoAH*xnY&0Su!Kn>z?fmy9n)7*a!;&l*> z#i26Y#suZ@TCyt)dXKVwT-nR>D19EMJ2A`IWmgmS(bgO!Lvj~$Uc_5)I~`62YU*$b z0*?q=4$ooHAg5FW7X=jO7Hx%hUU`}c8e*popKT(1Yg_l&a)^tEv^Nu@l!k#ns@>#C zwt?;B(u{t!$QpR02|X)86#CGS`}uw!*pBF_!(~%YK85o5&F<-4b%o%8qE6xF zy)rWOE~G1vM$5CdW75#lS~RBOY-a8uId7g3IdeTzQT0V8ZODk_I?l>frg-{Ix_q@V zCZc{l-|+QjV!lJz;%|N{kjA>D@mK!{xGBzH#s3N=zeBBJ|3zXczWxmWAQ|R>VcPoF zPT4E|1Mh#Z%>RWtNeBK5bxuy&%~=)LI=yR<0LN)J#NiqT)Smz1t!UD`qp-l|1wVtM z94gwqP~X+VDO;RRr2xzmIE-0N_O|78?f(I?W^V(cEhxZde^0rf4nTO#Q~nQ7b{mXR z0gy?0KUOHl5E$iuM)u*KdeQr67Gym`fnVF*O5Bi=0a$OZ`Bv>A2UL%I*oegiRF4pR zoKZs@X<`7Pm7*PoOe(|Q{HIs_PwhdB^|Du}e_P&yrL()s5r6Z%co{ulTPa-m0@4HN zn620nAr$fspaW30!9w(i#(%j0k(}c&uzIrk*qSaFj9IcS%`W4_$YFXZ7>7j!kJp3x zivy_-J6HIlSnOD`vD9DX->-;3XpJ_2`1+GWdAD03Ik&*|u&CcGI(NsI^|bGhVjcob zAury}$5aNk)(wHZlz)#ZBrr0iJo{J#7dYvJn}^OS>X-uh+{KV@ZqTsEp{5X%MfneO z6|?1R4*N!!k`Iau%i6KyGQ%+Ch^x$6r@$op_rj4%35ceF`yn?Iz;%7gR>X_MF~bS$ z9&;)E4`z!1bHb6D@78UY17J?o{O^iMM1f+Zf%)Iee2wutu1ezts7hW795eoxNk27<>_TK-jKaNb#RX&s_S+GgkPd$@(E4|+E2y*Mz(F4!&jzEk zp8#zb=a8#VFJS%Lu#C;wNZ(veVQa7FE30CeorMU?yF5@R`9w+?g&K;t(M9np-9cS$ zpyDE%llv}e8h9@weh=6>&%o@*?l{*sc37FXQ;RszC|AWH{rN+9OrsY)wK8Jzk@MSb zPuwz8j3SFfDL1~WFWUT6x=Fa$&OPdX0`7v$(SAdpEj6+p+dN+RDRs3%%+iya%^P&* zvLq9b#pI(myisL!9JygTu2`$_Gu2rYqXN8*~~WSt?IPNtJ)`)3gKP?|I2{-Vp}jwaN>`g~6cYfB+$^;6Ua#dM9^QL+oi z2R!!+va}j{Qv=huNNUwfK=fEG>u|O4&|~{B-Ce^|=e>{@$dSu(n`KZELAs-&r6ZX~ zzhLl#`!>M<$3(zjdX$Oxg-7gn(~J3fK1q+?&CxA#(K;d3T5mvwb?^K>&uOX$YvK(| zjs4-WB~r#{`^`OM+JG0pWs1zLQLj0+(s|5*G@g0&L#o_wMy4VS_x?|Ou_RX3>@K=S~R+G}V?qo#{9jS;w`uw+J4#EuRt@*aN=x5vZT z4=DF73Cr`1?+?qZ+Sti#(GdmDs+J_DWjH{_MP37cV_Vt-rTVzu=M1qPEDqA-V`L+? zhc#2YrKu>x?Qe9SCWM25Ha@(s%R4Oj6Ulw|)|^6Ma~F4BRR~yvFYA1XY;;gP`8vaQ z_If=b+@WTO{5|rz8L~A47~xZn2yNcWcYAAtji>k=`_JE{KtoIquwH=06+;!#rWtg9V3U8j7FIRIjY*ugJ{-C9}?+6d^0-?BN~6DgB!Qu zWHSJv1$(XoF4??{^eo;PQiC#sZBoc8l>d)EeZ^&DMp!}!Nb4P& z_RmhcG~DCuyHEa;v~Zrh-NAnP3kTEm_`Gvf*J`}g-|QX0i?hd_xU5dR{kx!>$3A^Q zuRInK&=>Iic&$$bl`bN+Q61}Z@wnCZj0-}aOCGTqn~l#=eVz&2y%z||DmkOXrK}c{ z)sI7uPQ!eM<-)W_ba=tmhK(~C-8=NxO_IZu6JL7t1Y8q0!{s1+r>JeO}hYw6TS8cYSGnXdpt-Qh^Nr#J!mS`rD)w&3}3>{wsyZG&= z;XVF&?FeqZkX^}kJ;MiRk?SLv)cK2vx@4^>zGT{wgev#Ue*TK^@uoJ7wfPoYOPz@l zi)Guz4H}7tioF{NEayIu*z`4xPx(u?WQp2T zrXMWCZ3%V!ft{(TVDoe+({_w>Oo=ue{6XY+vVG5!~ z9O323L|#LF&XJ=8;Df(HW$Q7RU%RWG9kpoRS}woTmqw&>zlIS`P8jQ- zF$QNwm6$tc=AJ#QZ~=Z=e6G>MUN z1Bd)gC+vFw1o0|5JjCi^2p;AUpzd*D2=K;KV&u?EXN>5iAGFI z@>32=Ae+lEZ_qOJtFky5QRnVhuQ4R_{fpg87O70+<*7X*%MSrGDMZoyXY)CNqC#Rv;;Y342SgJ)lDdDT{@>gx6}67GBXo~+i|TK(RJ@qNL0r%4un^t>vh4>u2k z1Izm*Wx{}YM{_XKxAUN}U_>ivC)Sj5K^7_DEDRQel2@k^N@|PHp<0^H)Sqz-ii!(G zrdgz>dp`_IP)|A`Q0UDnZ#2*x0$qSY)Ql;w59gg`?3SD$+qpQ=6+xWLXaTF|^Qx>F z*OcWfti0Df;PaomTUZIS@?u;fyW2?S%rlq;rznc;);(}WJU;g)%k4p_%JxM*=qXto z^L}`Jm9quukCK1R{=)N8-2Yr>JOAPS8%d`DI(sCL`5m`_m2%jezUAjN%`Uoc>3G*Z8Hx6 zSbIg9ZQ~5m!_*c<#YOM;gYz=WBn#rtgn&Oz5=)g)(_J>rvQ)EM2M_G4rpe^Bldn^D ztX`g@ygmGL@Mr+JB7_|x3=zz^kgKo?EEFtA7-#5Z;VE-uM2Mn7v!+okMe7ErmbQmCMKHf+LlLjk;-aGb$^&Ge5S|+o zSkCmG1>l+3q|6})-w+MTf_ot7g^=RsU|i$Fd##4mf7$F<>YmfaDVaBKHR!Zb?Rzb5 ziW>IjkY@_yR%XRB6fro#_g--%<-vkHoAN3$;&69@XhBF;B|ys7=CN0%-=pPi)dxGW zNMxSzXpNKk)7{Eot!c-rgJM@z4y_k(KD#aCIp6ay{n|Nu3`!Wv55Ib<7#)YcYkk2Bg^RDL*Wklmom%h2>R{IO`HWw!l)YQHpyFVenIJ92l zra zVxY$;zGO(V^8C?3fp70oGXEo-DVS`Tdq(QO9t%(AvS|X7`J7j$6vmlnl%%_R7v)yp zllLeqQeW_@DQ{f{yETDC;ep_3k~6QYP4IIn#^tO3(cq&q{awSHoN`Zu*aH@k42AOS ztb(#&+Rt`WWmXx5EW~auBNp02i?2;cm0y>0+c}Xwbk_RaePG=I3&M#$lYh3SQJVew zO(O2K7D69YOSG(Gvn0F>#?~=)YlbVoSi}soBYiC#Acad`VLH4;(-RFKw$s9ijdcV) zr|Re(5(Zx$qc^QIfbG#Q5B)qO{C$x&pr+mO{iC?B=e;>Zz`&kG zzC4brdS`K>yZ8~M`kx|pXIytxc=W+!eUOAPMVm=5jPd5eYD6w~H&2<(>bY(_HFVxRk|#8{SDAHU0YW5&WNIiRwgH+2H9hnY!Fm4RPObR zo{WLvok!MGAhn4 zgA@6NDk7yrX*`gS^XFUssta{cW~h=^Rj81sn;W_p+Xw^G@!i)w-U3*3C&`R&E@2k8|OR>5CC6gZP!zE!2ICYvkp$dfB4A1H?+PLQA zB2!F^Oj4hTD3_Zc%3iIccvX!(m?OOChO|h>ZR1|Wcxhs63?BF8$G=Lo>%l+Fc$bKE z^M@Rp@Cr0NRef^(k>_%|S!Mn}`f|Cdw&rm^cw0}?oG_|`^z{v9G>HM_b9ZEJ?ZXK! zZyGCa03~RrgVO8G=B_YZx)<$w$EC!nW=F;e`}Ld3#EFvG9&^E8Xvl~Svo`UDbsX9l z0+*8Vl9lHcG#NWXZNlDR+m2Rxb-Bfx$`*-Ti!*_3DlEUi6K+5Tg0=WoUT(|ykJH`d zvm*0mHvT<`tc=ZqRzL;sqbz|3!QNW;tf9kqf~7gvy4(!l!B0<@3KzZJ;!CSc#Ndhc z^TGBGZ~RN=Wtq2q2%_=9r00?w{+L#l6RUID5$5iX)Ri6QlojCR;kl3Qa}LskuQ`xwA1Ri;7Acc3~tvo`av?v#^YHWx_Vcbvz)J z>sID1?xPx-{t@I(UVkCFWR9D}D;jk>q|TzgJJA+t;T|8zHaANNpvJJ~KDAiH!2#U< z1ha((hyidgzg~qLDy-)BA`7QnnpcXZ{WA2!`Gdg5ZLFP?{7IZ%;5kIskyF5Ck16Tq z7uJ$Oyj5ggU1ZVNyFy_NfqQ`sr`!9NxO8CQmOY zq<$w-{*V01|DdE(!}0KH88HCzd3p3#PA0U1|5IlM;DswKE$v9Th5H{#mQWR@Z@r-a zo3$O{f0?c@&fYsV7&+@=P4NP2`3b5<8U!5%glKTG( zg%vNC4f!2|4V@0mH~{&JE(UU-4*o|wADQCoUm5pz_y^|q|4?!lfQH&Td_q+x^4B*9 z>tyV)!ibdcl@cumT1Z7ah9qm~{2)?EHqsy6!AjLb)|Zm2WU0sOmTZfwr2No}WIw)1 zfPg~=n1fmdSbeAm9cd&gX*g_1UngY78A5>jVFW($bc18Dm*6n|(`qc2dBW|q3kh3C z!-6Rpe$mo7+A$_J76lMcl~d5wc%L@`Jjh3fG||RwBPMYVQK&CpzVu2+QbqR3iRgoA zS#SXk@nmsI@rjCnwxzoIB6Kw1Z3X-&)cb{boTQecvZW<8Dhg3*Ip2DP!(<{o+-mvh z%iJALb4!c4`!<3`N@`Jgd46@Zy-@!@#J889B&B$2W=809y2;Y@Vf?^$W0W9MGHL4A zmIpzwzFvqxpe62fHFS^wAf7C*tSv1oi=^3LNt7LgK&8o%O6(bQm| z4R;eIj68d)WnrIA6+^!FW{h6E;u#(uhE`Ppc6svU3s5r!ta6Qwjqw0MUrV4g3;4umUZkKS`2Ol@5~ z&|oaE@dR{t0>aUqa$&Jzfq+HA@W?Q9h=5wYI0srz4v--Nu8cp%jG#R;FHp$=1lWd! z72)puAC3$X%&ANgBR09bJR(0pAPJluK0ZFMySbE4(9r%__bb3~M@GVz;d>~fs8A5& zWbzi8EHJkRB1urbGdMju;;>m}0n-!~7Dhz)A1O$1grf)tQBf#V3R&{Rk*T@4VDl8L z;3`oolO6^J={QF&Y2*+5yrE|v*SqhoiKX3k?PuT&`ddO=T-+g7DF5OdBp5Jd=SuHK z0{gsWmy(()F9Glp4<+HzS00ZUO>7(&CMxw#oQ>py?Kb;m$E?2YGL%&Gzr9PIcXbp&`88mF|n zIY%cKX6xUiu|uT6<|wcsFrvgpHR_hjKy=26kqXG#(4VBIAA9D0&riVM;%+G{Dl*w> zw3EW)iz;?aQ=<_9W?70nz+C;lJ$nC1tv!01!_m=Ej@|iqay?_&X8YAcC(A235(9N_ zbazJGkRhbL0o;B`ch`sT+g9la1|t=VK>9xr{R{laR;MMKBqdrm1HAvIPjN$N7Ib&C zV6`;iQR6Z=4OZL9tQL#+!zF6YNicB~HC|re6BX;7@#3hQhxgk+?<(_u?9}ndJ-qXicY9?v;16_zgVd3{u(==m7K`!*L%Bw;gy^I3nQ+gdw#W!m+DEJjuD(+=v>U z&0xN0Ykzpv8;7>VgANk;CDi~tZon_lzHkPan@jZcxa2Yt^$ubs^!e!^qg^G?j&xU- zZbj4^tAF^)?_fba(O&6yQ%2Qpdr~)w$DR@<2EEK;`wT*lv3(y@5Ie}lvtAi1x~PO-km$b#OR7k%SxEm8`1O`Nv1SKhC1Q%4mrOrNU-o&v+uP zgDkwyzt7a+kTKUzLuV`T?$k)cE8aB2f;7CZyGF)XYd`$i&I(tr>fjkporzjkro#9N z=k=^T*noQ=!}D`u8pn4n2XOXhdv-}aI~+rDMV%>IS|Bs?W1QWc@nl*!Y*g&@CTsa`h zgQ9X~%0$kywwVgo8bkQoL&^P!#Pg8-yvFB?p&DP_n&(~Q%CP#g>tVe9vGDa<^ql)3 z0qpsg=+3*Z7Q~wj>G=2`+M^M{fZJn8Da;g?EtdNdZP*Kn;wSOBmgjmvC&l3I;4g*3 zs<_2kC+$$i?QX<#U9ERto?mwfY7-4q@2m+t%2P)wT9d*}g5AFLNxa@mM3huKBkxbJ zs5!0oSdN*CS#MI@v2NhIK=L)7O0?VFlIpm+lSLm73e*RruhRYkSGIKMb2v$J>g&^L zb44ku;2G#ywkK^U-Mp#f88lAA*&QHxC`+~QQ=?4D>_;-}=-Yq@5=XQz1 zReSEovb@zUWLGcm%pcKqZphLHx^DmI&rea5ow$5puJN?j;e|Lq4pMJ#7U{p&%*B zcF}wbLmK>$5*SVD5709Fw%WUVwdsu9kp}a#KY2N&@auQ-67o2a3Oe#%DbWn(qwxDs zTu6w{5&9#dzQ%oBmZS$4JztF`kQL{WUgeZLT)l;Rz1}HFd&_<{=%cP^3454An>%!9 zI+~q*ekq{eKTtzCx+ZBnn^m=CU?gVsyzRci;2IXObHRL#tQ73WE4S z_6JYOzAM4;vqnOa#PlM+E3X7PDi5%t}_l z)dM55i;f;+cN>;}_I7(k9pxrKxR5n%I(B6o#jn_1?;)45e^~IutQp%=wUFMzs#W3i zwDOw>uG2qV$>1w@j~}3raV}&)qpyIwDky^#0*|7C=x?S;Kdo_)Oj>43+JUU)X2`Cp z<;g;`Wx|<^C`K%-2SJN2ry)|zG#t7Ik>jZ)_V-ds2ny~a)L{-Km&QSn`!c+XO6XYR z8*qDxHRsE?Y>g|3aaiMvHjOzyL^`Ryl%g{vK<4$|! zUg=eFtJ#g>a+g}>(_=r5_kGC8Om$qkV#4M2NPeZ&SitjjbgCG54MxTotK>d%pZ?2> z&#n)_GE8p2Et)woGt!~UhL^sY0;|9#1Lr_?O9>oA{Li2A>gwXg%INi0sB~0Bn7*nC zGJ|7PdCI6%B_eT?l0fhOSkNyKGsJ(2|3L&$TuEx>v88w+= zq&uY5KXsM9*kooNWstV^VCc0W0acyDar}jFEKcXczDzNVwl#hd7M+=B(yV{!a~I@R zy0{x~$l$Y7+~{hTP-Pyj%V+rUvplIwZE^kNf4Klb4>ynj;h3fz#Fpm;yZYX^FpVaB zHAAnR6d1lHT18t?km1Y&+)3$dK9ZdSTR0udS#}LW7gsf&7V%wHBZ4cfbQ=zJb6HOw z6-&1}tEC5p`@xws#PjE$)?0VKMm=7L7JWX>biM5JG#81|eLY%zcG|Ae`uK{=&bw>3 z>e4YtQxo*|I!>s*=E{PpPL-nJf}-A-L{EbwdX^8>^cpSgc}rNanPi6B;Q2=XT-ur< zs}X(47wzVGORdUCbp!k6u2}P7oJ3FU565yska5bdSaHkc{yVSH?y`DNg(9?krmq1< zzlO}CF&pX>`SvNv1 z-(byR<(}v5>#6w6dzWXC%-0T2y)drYVFzWF>x0)9eZKn2rtGV@u-a4-Ctlzcs96y) zHMi0mOhK@DJCLDwn>Lb67OoJ8Y_Oxo>*FIHyedszQJ2(O-(zxP!8RL**X~+N9%!OO z*4jFkSgIlG=Q_3hCN zHlAC`&ZTa_`>CmmAZbBJLE9a**J3%Hd&keQYhs#nW`AuZ{K_ z9K4)oRMtP0BK6s6if};CcXvsr%>`nh6RZTYae~jDjH=biBZ_+^OvO0W-JfZUlW96S zn0tDgIilH|P)Rk1Qh3{$LLYhKM3hm@A`!v-KXqPtRX5=c8O4TOUT!wS6L;c9rkaZ- z8PyOptpO2uoNYhfC4*xUXKHpAsHbp64`OvoZ^FMV~AgxDPoSq5!M-z>h_- z+H*?F?e^Th>j-wJ1UrnlCW6>^8Z7!S+icpn6^Ci-C>_{b%&=jg2?@UU&fbHsNv9W9 z+jQ4*iWpenidKyBi<&2SeujwKnR>_^Xh%xM>m~M(wiE&vI8OFnc>I>6i3`6ceUtQM zs=%eeZ=`)+RKOQ00Y(j(vFnO;df!zl8lKm9BEkdDX6g9^J@vr3?}Ea;PnNDHulat( z0TflBwdQpV?pySiXu`R+uRV{duHP2ho^|VkZJy7!XV7o&+TcWU*c!>lCRPTb6ImTD z&968glQG7I7%6PnEXCURg&X|-Fx)JCmSVnvf0KD3a^N7>N`9v zv9z>Y)w5|JZ92^YEQYayyBeohY0_AO(53Mm6NPNkbcl;&*y<@g%Py>hkrDLCv@i%+ zQ~wMXXR`Sji@{D$CU^oDDw@5ja(b~}8g_idFPMCR69 zkR0eTrhN$ZGOW-+DF-;LlzB;4SgTamZE zH~T7zK;36_-Ks#C=uQ@$^GLstj;-s|9VTVu;sos1Q})k5C3!%Mw`oHvwP?9Vgil*t zfyj+z?2EQoC7c9Ll0u96rkN~BC4NJT=9TM~-1d~gxkfjON<}^ALtsNbe`xOZ2!&}v z3`d|!%r$_t&}F8?J(D9~T+{bC^S1E0<9lv0pyJbUM=Z!>ijnnsldpbK2U%(;@=#@- z$sGSFx%b1uEoL1&_iozy7Z2`w=?#t$HI|^gJ;S+zBT^kq1+JIY?5?|+thED(T;VF zu@K$H%7HJA-g5wW(i>Wo6uZ3@1?|F%u5WB-(b*S{FTqgw;q}@!$%kuwGl{+X`MF#m zyP7GpV*EC{&tPLKo-yu=>+oL7h$TOB6}$=`YW8b4UV*a+?7l^@bZ8%oq3J$H9a;B8 zBKi`-ljp0iIqFlU8aC^Yp_CxBOYunIms`qHPln-i5R=YXQICi^|AxtRkLk2&jM$fl zRnNzw=OeQhf&AOq%<$_FO^gRvEGJO{gbvqGS)UW4EPwTFE0vXNQ4>#hgV*g-|BC|M z!QW#I=VKLC!#0IUV;yPo16V#(6znTq>>X2bJoV!Q^_4Pclx1~mO%4=GxR1>oO2?BUw$UPg@Y?x)}+!>oo@=x^rllEeyq2+MwO^z(<7C z9eYlm36jK-7>T@t!$YD(8D_6XN8*^lE~qOu?EIlM_RCXA>_ed8i~(Lr|3YWusgJ+q z?DTuz?b=Qq5G`zu$UD=TzIy!H$KZ8S>`#_SCRLXr>+mnOqy6EV^HYmM$4w4MHw=MT zc122IT6Cgpsbs*dJ!B+SFx^+NpW$)3{H>v^=sS#JL3^IVT!oJGN#J*z5Vf`f4? z8cjucrVUfOEL$+?Lgal?MMzGT*(&x2w-YLNdpVqu2@O;GFd<+e+qoZ4#hcaWp$?Ol zq;IYt%n`?A8`1ViP@2{!dkSaw$Y~xGxyx>x(!J6;y&n$e>8|i%cz6}{=s|y8Q|n~h z`8k`7H*}#_eClWLr7JvYrsEO?4#LUZq@xQxC$o_8VyCm0dy676ZGq=o!FM7ynsD%^ zKtZDZ2m)yb2j;dN@J?4AK!%k3{~_xu!`fQHZgFVQ7FygX?(SNoxE6OW?ruekTX8K8 zMT$EV_Y`*x?hqgZy=hO+`M!Jak33H@NoHox>^*z-de^&FwqlZENm+!HR;>}Y5pMWvZkPwTp~ZM~S05(1UmN@Bs2*6nwG))s6S!;UNXzhKk$71Oz%e z1f5~tH9S-Mov6$L6_!v0S5F*r6{3hwS1z92eOQ0W!F>x;{&HA>b+%Ya3}A;t(Srf59#a%k!T zqj8Aajq9e3q4t+DBDGUb?g!ZzNdkB7TY@4#c=_JWd42Iim*Z;<`eb8sAsBZPi=j98 z(~XFpf#FL?FLdKmn~qim+*adbjFMUCW+%_y?`K(Y?Q_$I?uwx^z4rcdH@K|)d!hSG zsWBlGC_r&)W~dy!sC1Dxu;Ciobb?^5#<1dmZ*RW{0}pSqN?*+K`*!NBdG+7V*G%w& z@dYfFgW7a3I=`_vtm@SM^%sOP^>RGM0TL*ueyeYvG!a;2$)vJrfUE5va2CZvfLtivek(^NjR=J|A_#MkW{h zi929z1#Tn#B}M=5PP6~lX_<|GyZ=A$uJ(9INB#lRpv^(6AKDWA(-t{~%)g9qJcNJ6 zFZ%x1fg2G&#OK%9TcRr2dIgV<6FVF|)R;SuTwUn-j*XF%KrhVSop!MAQ|0I4uzr0)(lti@E>~*1G#Z zpJVKGFy$ zD;m569Sr*-(OcLfj|QjH6*?jjuVdC2y?^@R-)k`F1(DNzLH`LdQ1TYdx=q*PWH%gB zXgF8IO}NpV*FdG~uBo)^DuNl} zbXm9gX4D2^jJMJzZLL0;KJ~FlEV+xMx?xru$<1Eq-2^QKldN7|ay;B5#3YmWFy}rS zt;Wanc<7-l-al@7@is!kLlh44-u=%zoVoNG59QLJjwB=}RhTZ_VozaI%>XS9o|Udq zrJm|D4cnW$rfkRWyO%A2s5}p1KWvP2Up3#Y_!6$)2XEB|owS_yiqP&d&FR(5>&|7~ zP_AWlMHxQ&67N0sBFdwYJ~&>SvVV_)e*=*W`YB)~u$Z|OQ`y!%og5;L5JY{#Z)Amr zo&i{)rOTgbBWq{N(o7>L|aW z0{P_RQN(|rwy368`E{eRY=?EAy;YpWM!OvIX2=wY;JhE95E3s&i1=e}I6iqMtulc> z%}={P;BAQb4OSC$ZcGB4vujN1wjAh6y;Ptkv2zTud>S0Z&(drxBGtFo0}r6GX->^- zgbGsiE10eh!;77wj1Qr}Odh&MI74AWj?Qy#HoK}_O1sDj9OZ=dk7}r2Lm%a0!)Zw2 zpP8Z`^B!l816C}ew!%vC2^RD#AZy6R_ZI!tbE59uPLo6{Evgr0`Rpg!B~}yJS8tI# zn@1X)!NhTOl$mh!(v54?cx=6`yvq1UKwyz8n7TAlv%Sa#UL?;>lVLC0myzQW<$K1M z{7WDjT+)Zx0(@eTAPqpJ5ruzQas zZMd~dP@Lv`Kjv8y^rggQI9buY#fPSDH%$Arf{|8O($U7QE ziuW1U6(61Mmv((E|6<6mjy7U5Q|ky9{07fn==-yw$~B%&o<2wp)|e^~rW(4v#g+(M zA277nmz-_Eb-ZgvS*`Bs9v_{ColA6;)+{bloTJW>4ESjYNsCp>?de{27A zzBgzsbKTGu@y-P^X9B8ReulisUJ76Prltnr2`d;dKHY~jM&95sft;)9FT*h!l6k87 zz+T?6>*=`<%X^>vl!u>Jf?S%1H+G|4Q+jT%kygF9gpwS%N1Nl#9H6)l16*0Z3BGpe zc8dGlh!av`T_ybb_41qN7NjbzZ+!f4&uxqWD@C1K!#EEcz>JlT{ntvgyN}sxC0!ob z=boLn**52mLOtt`_oLz7oF-{k&m6PfS|p|nCRbO~mvn>^bYCQkCDnM8P+rt9cW`q|kEW(s%1@A)nJ7Hm@6Q_W<7|vLyGT!iR?;$7b(4 z1M}`-f0C8o4j;)PhBT27YTz&k_x$IbO8k+z4VXDaWOe8RP0rZG8>WZtPNa6=#0K)b z{xI%DP9LgL^>zZWcB1Z_I>DJ9=pZv>(RNSw3hfYUx=w5C&yqp@~LH=<`1PdPoer^ ze(>_dnjeYb{i9Re^!N3WX)la4`g(=@BC4yos?8O!lN<-^;Vfp2m2!b z+eeQ4wniHjf;coaJ>aMAp{RkiYlugNCKkiMudc7||cd_%JUKsp&h; z!|aWTFb==n9!yHP))DrY6tXn++FKR>^G0`2;F*`L^@6ya7b49Jpj&iuZFcizBmwRzP-Yz$6*m%C^=TrduOYxFQUnQn z;>>-#)5w}M#!|@`(M~Eeg@;=D;prw)mfS511^9}BZQ|gJa2pvIo;y^+Ou!6;LE66e z*r>D$C46U*qx*N`YTK%Bfne)-q#5B8qtY!mu>NTAeSid}Q>W{W2{rNu@jX3JkX+Yh z=WI17|IQ0JYY}>L1|#B<_(PW&26^J5TRVp6>W>eVA6y1*pL*H(XrqqZ?4wQM+dQaE zzN1-%tab-lG=tpxclk0LNYXJws`bjt)&;P}ce>2LNp)-uca_&1XE4CEd&@43+k(EK zJ(YP<^y^m&4s}SV;8lUyavq`k~^Q$e=-Q?M`osY;6wuF)^~DRvO!~ zhi5giyjB=xnMi-#Vvj~><;^@v7Y{3?&L`{4#FC8LDRDbrMt_P;<8(cM%Xm7Idj|1x zeT;;{ QJAsW(wS2XKsUJ2F-8UETl16H&u=$4gY*(*vc-HEC0gA~cJugaJR7QhW z+yFv?XrlNuIqvMF{O*i=G-|f<^F9rkuIH#-@%ylGD1pw4t(5RP<_H}fWJ@uOzo>C>krl~5swuo_*eBjd~Z^E%R}!JK&$8=~HB|D-M(@(@oz-R72L23LTp z-VYg2od}(pF#HKa6_` z`jfifTt5zaF&}gE6HTzF->=g5&$w`Y?Jge#wagUxATA;w(VP-nt$YG77l^(++}n-w z*--3n{h)kPy0uoi>qBGx7V2KcsAjev>_EtV@vz2u?3Oi{EPY^SJG{QK4Wp{xk#!Ta z7#)k0rcw+cR7fugHu^BXC+>v1Q+9g_r( z7j?}1IC&a@bzrujFf!WC8$Db*P><9I6 zjz?eFmNGII&ig1=o))mja$NXa?w`Vvmr7P)NL`=0ip_C4jdPUV2C^neEuPChQFBo13?Uw5PVpBKobM_?8M8r@iGDFqYQE7w)t zynzlp%l=Y6iqhPz1FbXm4SEJ_F>wW-aroBVGAgVkqOT=Mzuu0Y@ax9S3>U60Us0#W z+`HfzR9qWj<)whNAm1&D@qo3W+n*zu0D^~N*DiTZJSTkn7tmeHCsn-w-OXkc zUmzZ>LvYu3W#6J7HC{OKUYK9)>KIW`hPS%GrN+-b#QFs9(q&0mH6-p~JC9WG>wci0 zKRjuTPe%s&GA{9;eu}Sl&v)&lZ7<{8Ns-gGOS4Nl^eifK*@xc?9_m45RY4Qj6cU-d@rVyJi8UQA`{owI6-R6E+Jq9NI+{+bHf ztQA^rkQxV$v{aujTWVgi0K|tEUd;T!Cbs%Eje8lY=5t%E!C#)XLTY;b zROoA}i{#_evKW-p(JvfKMkeSF`|PWj7~tcGxb9D}Za!c!EfP%ZQIB(DJ>Y2rg!+-?*Ux$HO=NGN8*)!rKAoe`F!AP0 zX*~xJ1Fo(7&5=upT@dO9HI?n}7wAP^9a@JL{6yu<`RExWUVzFm@v_J@zb9;f9j9>w zAZY2EbU$zVbHd$eS0!ovq8Yt6ueLP5}1vfjy%99Uc%F&k*J?xx9+A$MF5t3obu(S4MwhJG zR{=D0i65tCYgn^H+qvL1pbocON*?IJD^6cO^Lj5Q$M%)_1R-Q(J>M9D?99FBHk;t>E&sOjjpes^0&t8J>1!Ss#c2S+Mf$m>6}}L7Hw0Y z#1!fzD1{5R3yQ?GI8>}Q7E@Ep3;nt}d#W_-$gsY?Mix0A#a*vt0SxZR(XJE#rZXX-DISSgfBzC^`Y=uPf(ru zfA^B98pMR-7vPVqYl}Uanw(1k;I9Fmg6>y3V%_vp8&jCkico5WT#Mk9Un1UiD(TuF zhvlQdiq!_ID>qYqgOY??p(rHS&v(HRO=xwMwikv+TDlcCnI5TT_&tTlbTv+T?A!yz z_+ve4yR`uP{fthMY|#)NQkW|5uzjsgfw@ZF?`QS3-Y`EWvTa>Os&c7%KAF+4~My`-;MS8Oh%0H=5qM3Kl`rN&8Tar zs;uR{dc)q$j>C5e8HaG9JHmw$(q_Rp!#3*xcSiXNU>JswmqrjI_DGB2D$&?ugNxdY zJ4pUO6&Ft&6dV)STSaCD{b(NRcK~rFz;<{Tpf_51WbjiZ5t6VK$M@(Q>5BR3zZ+qt`kS+lR`%+{EU(dCn91$}FUR0Oc%$)$>3;wgiR1Y3zW~*Ry>nou z-5%g0M_5V z$T}{;_gi#zt7ay1g6I(HbBBT5-`!GC_XqjFAALRSLKKrfz!N>mjel7t1KkZeh!xXympRo+V zLrDKiPBppSp#My1~B1eYIoX8Sqc@|1^bi(TMJ|v2sM)t{i>yy$c|Wn3i%7 z>I?Xn3-B{6g~)V^c}K;kIh^vJ*RrceUi0tGii6z8}(wO{}(tVv3 zT&2Q^7P{9=B=D1(g!rb}5Za8VbRu1F7Utws;A?Gds=l_R8tI(H@4;`le|0jav>(aM z|Dr`J8s`0F-hYAacHXT`=Q?C==|gz&<@N6g;}wL^MJ;L!ZD z2eulZJ-K>fZK6RW;Omk9Ih}=O(AfJ{2guLN z{|iqxU)^{~n=ZOXGq9>qiDENB-kVA-wEEtQDv?Bgl{qXw%x>&5esJ>~OT0Y_k-E!| z^}XrLp39#3?UN+FFdvJNzC;=gKj_Sd&S;oUyVKhug@t*-somOf5SyGG*=h>5-LHUI)VpFwhxl2KL<9 zX4jr?>ZldhD>i90+9&FC>mZB3D-4pwRtIpxc_6NFR40n{azVi6owPvd%jNeYwvHpW zw+(XXB&$v6PL91fM|rUGPQzwIdVqdbG*@zjMx4-g^jTc-;E@tjG|%+)>dT+?;`wO% z?p#bKLPlvJ6z@XNA|%K1iQCtQBX?{T&^K-zs_+E9_74K`j+xx|>bpjsg=a>*Qkl&3 z_KDF(oyg5N`ZRxoVi8w2^t7PTWy%t5m*Wqi_>uq^9Dzn%ETnY}!KR52_Eh)|q4wY= zbT+uD%F4nzbl(FTbeQ*p03bsyU+`%T=VYtrg^+*&c4#qYcK)LSIRE)^6=XES$(+#= ztedkkGF9ec9e2SQLY{f(7_av*gNr?zp(odEK6nMY^L=Jk#P=hhy38*Ik(%|rDtW{P5Fv_J{476FtA-{e<=*}Eq>z><$JHFlgJ&@P-s{$~R!Q{O{ApgXz5u%dA zzR6)bEd!G8r;`@uNjhMffA<5w>30SOAuze9l=p%=fh z=pJrKE2j8W?oeq&4(7{^%va943Os2QiI4(2aG)pZuZ|JFL0jfRIG}>=%d9F@!hR;> zyVV?WDM#wvQ~y7pD96}*v)3Z19?s~8(Z!;k_n4LHIQq(Y#VX_Dt+hN_(Dd36>C*!* zI$a?9+w9-yr48)9|0ANkL1X7HDg3Aj{jo%CVULyetkv#Q0e_|a%P2m-&wziJxyFm4 zzU;Y>a|(-OIT&cPE_`GGpV^c<3^H4eWhgkAzx1)aoxJDM#+J3PpF>wC>iv7!%|NH_ z>;P0Y2xs_9w5J>S2m~>5TI}!wg@Dd4&x=k(Jib2UJBp}rB8w8UUx20Gy|b@s2R#q6 z=!ky4^?yztjUFAPy&eI#`C%HYld`?d*6zI?o$sCr9easyOWg%i<5?;lARKr02Fz96 z5=Tg4Z#lW{mxaH{*zqD)OIvV=IkjLvS>+cHR6V;CiNBVkqc*Ov{k#dGit)i)1JqM)E__M6&ol2 z{OH`NzO5gDBL9V1Y;B|CqhU^o*{OJ^i=vPMK(M=7b1jarH6#j1e0=$0C`eIgz_*U5L7=u$6&x|C=l#CC9ySup<;Cy?i`U^p&%w`n|)9-*MToX72JE+uZ|S zcg%j8Fi6`e#XHu9Gj@O_6&~CyMwWOO)Q6%8?Yx5sWj)>t86KNnV|VjAdf)~w2s&`@ zVwHo2mD=?cso%l;!|}ZcBANHGNvDe*I`#c}Co*b8Ag26NH*$pV1Jn~%MS z*8!qD`t@_8$5TmH4+IF$z3PJPh_L>+f00}`J(FFRyk^(6p`_F~0wJx14Xwlx$T5`r z|ItRSYM0}}m~O_-$1jM<#nV|H{pPNZyL8TUQ5SX8YJ*6JhkLAzRMK)va4<9p-NalO z0RKn&iG5aDHaL_u3zFZOm>%B?M|Lvg^b+wyNku=%gQ3I*!A|$#paM${=soqFFmcCA zxQLX|aWxDNi}su&fA;LPQygJ(dZ3>h6Ghh3flWG8!F2*vMHgFO=*EAt%+e9m_10i! zY|~OCU2Ual`^eDv1q^mGga#EX$i^9Z_Posi*;{{lDHndqz77r((vX19*#owa6q&2k zF2WSa!-oe-6pKEgHN^(XXMtbs+#gSo& z+gj6Hm!zAoNAT>%6>L4XUfBm6zp#Lc3TYcdf_~@Kp|Gh-Z3pK|g+Gd=YOvn_#;B zB`WMaz4;Xr+)4#i_7X@j2&a*O(r|RqO|U+7r-zSu*8IMTikLpkT2)^ciYHMe$)4~y zHri%-YP``uIf3Rk8RPYhjIcg1W?PeTsEcVYP=1vW9sTrl;jw%>w#Q=W%}e(GqCfCD z*i20Q>KXOeSo91uGB;3GrLyd+>e!@lG49O1Um4{YvaLG&!*H9m^gaF=SFi&NYeGu` z0_j2?r}-pdSd`3PWfMkshAisL*zuv%o&(p`+v^nM3BNrE&@!?F9>%`q`MOilM zh21qxi02vpO@Gj)%!{Ir`04tUS}7k4on4;&6e8bo+*#(K7idWqq@Atcs@tH1+6@fo zm5&@76Y>N?lLVU@NgNyTyWRBpmzFLeLxH3SRNg*pXu*zkZEi6}b zRITwZ>CkM^+7qEI4pGO)$A=tmL$8g8r`s?r?uHj)ej=n&&J*A)kXJyb7-)j#46p}m z{cT1Lz3#4Rg>#7(2!c;yh;QM9F*0*(T<1|rDyp)t_GIB_8!m>|_?%i}t(VP;8iNRy zY1qKq{L<@NXWcf*vh%iJGg?$7BeGOI_P8HGuK4}=RRU8S81p$c%R@abe;(@nDge@1 zLQFRJ3yX;)Tu;iCR`-6z^{)0pCWp7e#CE54ov^?b$a38m$pR|uFHacKf6mK?q&wd} z$QVBM{dS9JE_43;vhC=%%9fV4!hgs-zV$;H=7R}T{}}6#>iZd#|2`JzGCBCuX_4=5 zf@vK8e^P{aGBJM%LW82R|E3Hhss8&fHmI!gKNZ9VCI4fWgW`ntpNEVo8P{*{rSFwl z=;ue`&sbXs-OGz8CnzH@f0<>I-rV_{~3pjtFnQ8_h&4@PE~P zV;4yASBer!L#l;^nuh-#xk1tYvW1cUlZ|9MCwffm#Fbm(bWN*w&3OMBqj)Fph=4p- z%)U6ahmjOf+W<}f()Z5*Op4rbIjzkm8>!hPpSU?ix&%k#WsR4@iI8_84{EqdDdgxc z!DPsN#43&%GP&x;N+)*+_)9nw4!W7;B^{LV;Otw_Bc!ypvEmNl!c5I!`~2-Q*@FEY zg+Aq1=jLlA4cjtV_8O|8xmJ{3WT|v+!LhG=DvTZaKekRx|5D0&YnJMgn&+MXhEQKQy!q6(Sgk#@v+ z)>csBjIE9AH#peKC=OcM39@QlcqQKMHbPe-hgS{`u{Dk5AWt{3Ki%~GOq{y2pRCHb zJ_nnD*<~LuBjz(^JXtQq%wK@B3P1{W52UY&5KoXD^`sdF>2MHJ~VqGl`+ zS;T@}jx3`}my!VMYvu2e2QbHv>;CvZK_{Cz2|m^Sz-aXbr6iWgVj`M6nlxcF>=48M z;`SqT^oZ$x9%GN!i%8JtJe)4QnVwzh@K=)L6GU&!CQ}gX*d*DD@Zw9Fz5KbW?PQb< zws-0*V>d458x!7KD(m10lC1vLPK>2BD7omJ&vOjfLgoNxJ1pm4OnVrg^Y)Lc-LnW! z;=X3|yV01dUxrCzOY@zCjFBA6bsf(*uM1W1oeiMUU6&{|*!zbYK6}x*h44YfIBp;K zfGDKrGjk_rpp7Jk(o;02O0^yk`d|#YEteTtp$+kSEmS*ext5Tf#Tyl_}(<$wtj@rX% zI{mI9Oa41pB}RvL95j6ly4dZle2N9uto2mUmk`f}kpaRhwfG}S%IVRDx>`K$?$Op& z3_=21`O%HSAHmx7jT+oe&>?UVw=SQt+IMPqa=8N}ud-}LbS9SrCnb-pZ_E=@lRg;O z2j;;V)!N)k<#_JXtt~VLbJO^>=(mLso@&eAeE0GNO{p*j+-Up%D0T0WUQ(!N*_^MA zCvYK{&nDi^_j&5>Dep4|oUuHne&3DZnsLi@(E<&4!z8AlQw9Eb*t&6WJUijJxF^*c zJGVP?8U)SyUB2}ry0R_{12!|RE)BPr1zX$7u{HB1F$pX;UpDU0#--kOszuJ9nd?^c zW_X7mrb&U`COr^|_*VxRTUilkVW{JHF05@`uGu5!8o%=3(&G%nI9L6cEODKqH>V+) zrbYdZ@b?mgw%C2|=4kvP{&SPgO+bft-GJYj#A`fZ@r$B#qvOfjfHUzzY&YAR@p`Oz?BO|D{d)^`dEY&6+KQWhJqpZ0S+C&+5eCTym{;E+$Zn_ znHqFbA=2<%>QtU$Ng5EPKa3Z@ob9Q>?1d`}*i8Y^KzHYM0B(Ej*6bO+;ES`4-SK|b zL)%V|3j{&=jpgOD4W6@E))Ak55N>jeMS$_k#R-9BY&|x1lnZ);75DCULGrRL98*p^ z791T{+D~tlu5a2vKPF2kCzc6cM>CA?6sjOblO#$~7;}`e_F-0$4)$FjnuWF}V`4Z> zYs2GdzosC%Nk}@N#5>;rAKVJ^hvlnb&Ecn*vKIA8xXTQ(Zqz3TPY~}7mITiiI6Wl? zeP6)uU24n9hAy#_hI(XZAnEQS{b4(hVoWisJbLhfXrT_U>8#g$kKyzwnnJJ4>5RaU z$SP87LaQGW@OfN!lNhJT;2ZBdM9lA>cgGJ3A+1Ubr|s{5!h)ru-WcYf6{Vlw$j0Do zaN4x5!4^RtAp^d?{WqLga#g!`gl8Kg(AS-Sp@}rnIp!{5(^_}7p%6Z6TQ#ZElV&q` zfbJkzor$G$PResT{TZJGh+Ut!2 z3`>vwAGC8Iu|u1be@(KXEvq@ulYm6kGOWbY70uI$ZAi8-;m@9G6rzwL&5`jH=GYB#`E`B-)0Wy|fb^JKc8356|4yw%YsNRS!kC8ULu zeWs^>s0NB4Q02FaJMAJ!nh>^oq*cDa$dco==RIC1ojfjH_V&O=BXQp^^xS>F&@N^A zj@8AHTd9K^NzIR1F~p&j;5haf#X(uaN=Tz;V3bSNWVy;`NNfTDl0iAL8gxN z3FprPqyFV1D<`r#-tYy1D6QoeuJ${dJn{n=qk_kfV8Yr!vu^_D1^tAHEj zYbSEP^Yr$9Cg>aHb0rpPQpq0c2IDS(nHpf0T(i3g^G9i+qoc$0e9mNk-6OMVO^ls8|8cP)bBvy!~_yTZ;Z zx)`^S)o7fjjv1Q$S4o?JJ{~Yw!d}82IXWTX61nSf$S8%Ny-gYb*+UN~OVE-CW({WB zOGp$;YaATVY`yN+k9~V!=Ypa0R;kchmQF)WEqHSD3&@2bgU?b2pEArW-}dW7xX}F} zi~XG(yu%46{zsxhvQcUOu<-AB;dx5E1tpr(fmPm0h$Wa)bfc3)=%2o?oSb(tBredU z-dQ9WFA{+D66B_wZ-FjHkd=du>#i^qrFD?U8i&j62)J`##;DbwA1~7RIY4Nrt?ZTW za|*|Bx$Xd%ba}YT5A;dO8yvHmQ5?Dna*u=3NJk+*fejD$0eXt*rkn9O`<|R1A1@_@ zNkx@rFYDG>zFj4ThduVZVb*%1s?E@BWpR7^UGe$j2XFcc{E&C+^r3{&q4Ht$-F%tvIODNx~ z@(TsYPP)$tRf{?@;V*fD=m}l;u$n9t$+?#|@jqxX=`@F+;Yv!ka%Rozu1)kikeTt*k$E3qV|p@KKunwRrkUHx}> zqSTfXB1xdZZXmczMo9Vj*6`fWA1Q{VW5w^d?NZeuoE!9fTDiAnv!}dr2MZQ9>}|fp z8cordg2nj?*eFJ2Kykaif6g)dQ2C;FP$`dU5}`n8^IIg0&6;o0;78X=iwv~^&1&8x z741Y(sI)095DU8_R>cB|$okb>2jLG^<-}cJFPQ+iTW2Rg-`##q#LeI;TR4*ls8;Q=Ua_w0EBbv$(>PpMVZIv5~@vK@iC*qo=@zmaG>Q6jqG= zJ?AT&X_Qc&k+Ofhx$c(jO18-Yj6?NJKbHd8*w_zPTaGZ*mxJ{(2^4zVQR}N2Vub~= zbY>j?ash&8)^3t}Y_&#Dlq#vToCun7bQM<ky~}l4i#kJd2Rhln+}jzvXL&9D~uc zbPha`8Z5sxs*i$Sg{x@M@^o}ct=A=1W|iHemu|d2rJB1yxKb1KyAqKNbO#u)rPxJk z_*TzUN*qV=B^muJU>%x772PA15a&P(kw_C=y95?H?>+CH_HNgo^+%NKg|8(7enTKm zu1~)GUE@LIs>_V;tM}`enmFM2Gc@<+x=uE0)4h&uorXrQf#X-NxVWTn?<4pLoOCIT z7~tqa=au&}P_3HzwO$V3?r(>2&A@U{s%y>$hK=SC1s8pN5%$K%rvcNa+#TLPo&C!t zz060aWzsdWMPSCXunX7T2~*xYCsaoYVE_cIP^89-Q*%D;cm4Q?sl>R}gNO@zdwUmG zZCpPcYk3~3r}?=4VEy6E1>KJC$u|(5pfSRe6%Ar2MD$&(`?iX95$^d(uHGQSvuF6c zroE-u^;(gGV3zZQm#>AmaNEbKykMeEuT|~j78Tz;1DRfA%pZ>0OsN4a(XZ!JRfS_= zVX*@soxq@K(y zMEZvxkDf<>kYy|;Xki}d;vW31y`611&=+5Iv9V&4gVPof_Hm=n z$>BS}fy%xZqHuX?{p&AC-jysF#cXEkbd2j#Js)XV_Ve=}j#H}h+6}C903K%ngsamW zoj3v&*vcPK5>V)t2O80(;)a7f-1No~M(8+oWzwj_tXZwJFlLb1h*F6&*EZt-9iB#H z_t*E`o$r>G7iLJd>%I0sWpd)Y7KsC4+3n{ub?t5Y3e>TkS;x^>HR0>47e|i{H}obS zlBkSmD0pMTlhYSGzPGQQ&?y|<35LTdMZfQP&tvOcJ0&*zkvi`?PDfo*h@I)8|A3j; zSEQnnv7N-QRIlBZJx|0ydBtpwC?zF)+EI!>&H$7J09md}_I8{--eaqa%c&^@mMU2` zU$c0I;!tfJAgfnA?)a~=Q^(RX*XePF8e++*sdFDrTU>$y0xK#0fEh03mmX(UE^%Ay zqn6u_>vHI3uKJ*ARoNgGw6ym6!mvm_cqd|3!I$IQi**gPgZ=s!Sb45BxbE&-{1~mO z&5DWWHcR9#j7ZEip@&1m$@e%_KxMU64F8xV@cP!*N`mK`@c_%E4S7L(u5}1tQ0(Bo zc1I%lQ@tkh?&IHHyf{95^V7GocAql~Jq1eqFE9nS2id7Ak?>&>vxjcS zm#K+m?q@z6Uk@+TzNC<#3zyoV(pcyFGRwNK!76|_T$^#{z{eg8U$Zwff2mH7{Nl^q z**xmu7WnqGDsLN0{SjAe&gHvMNnrQyTmEY$Zzr%m797&&jAQC%k*}#u;RbL0w&QC1Fcn;xha`)P0#Wy+xV)H7d$D>$B;!UAj}WjylkAAP5(FFHMq zDmKq1NYWkUtS`P(;?g_je(GoQieX*WP{?){QIBsfT>Ah{c!Z+jb|;`3j_j<^OOJzG zH1AkFj0u8_x%w<~_ejVW`$3yf`^P!ofT1w+qi+$$VJ0!bPo>VZEnG~N&CS8`n~n{Z z&C&JN&*V8M0~`3h&nvWDo?fp+bh7;U99Fb_E!|(Fon)!N0T}(i>^WCKGq6Feak$$C z!B;;0`=q(Sv45OuBWn<;DW@j`&+35Gw2!8Rf!85^B)x7_kzb z@*K1OQ|};M=b;WW;nuq*veZVQN-Ed@3le`CMZvd6*Q5)=iW$1P=ERm4U#>prLS3GI zpSw`=oUpfdWwensffro*bNn};!$w(P+Kyku(*37<>bXs*-f1pyv4F!W(&KXe8gaqid#{@& zwT~|#Iv173^`yLDs>0MNYye(vX`z-OR!OtZX}SR~HrSacdB><c6_{=H!5jD2b(eA#~?@y41H{tpqfl3wI}fvZrIWU<40pt6^&P>;>H+)p~Q z*v>G~Q=~D5_co7qbh-rER&g@LtEdEds;MIz2DkmG*S|wY5pku$%Y5ge98wlAD&jec z2CKW2Z!}%0iA3Qg_R@ApUZ|A_={nU(_NxV-tj%#i#YvV!oP3|4K8(hp7=aI0)LQ^MVYHUleL zU+-3#wJ7f5Uhgz>h#*+E8tSmv!y(_)v$T!(R!G5hZ$z()qBk)lyQTG(5-TG%MBlFaM0F zQZvw?+=*6LqJVc-)!>VaRMu7sPbH;;CDeE zIo0c0v=nHu)3*g4K4ob2yqi=>!@rw@{#xJcAK7zg{4vj`50bL}SSbP}loRy;I^9(Q z!hidqM=%RDuB7}MWyL5NM50anw_)D<*Or?9r>L)vis}oy22n~$5s(@XDHZ811*J<# zks%!c>F$;k80ltcq`TXpqy=&48hWUqVTkYI?|a{O{<>?OyVkvP&Ux;6YVS=B$hzD< zmRL^vf7I}(kL1(lZY!m?BcTsdtQ!9-=XLRHyhFbD-wLd1`-Ct`o6fd~9!f7GXM9nm z5QMw!8p$;u$~6&{?{9s#@=`>1{sF&=SXt%2dBy+D#XKB5rC<>}@HMtTL!XxvHZn5C zqNH1IO7fXeiazO4-qUfTYb`{!1&vB%q}6;)*jCX_joE|56`-BHi(p_Di9B%?k6>3r z2ccCja(9+8)>{Uf8DBTva0@kw(iS-f6IcJ*$kQcv_Sr3WIO|cy>{CrK?0OH&!LbY2 z;!^bmuklZhU$;o7^+jPqs~x~q8Y;?CC~*e|2RpiuPrRL9!yZn8HQ)@-gG(_4Y^U7E z5$;L(VE~OA#LTgOP{YFJOE$b#z3H@}>$Y{~vYKuzP%QqVR|$QB(R*R!zlo8~M} z!vvfFeS?Et_G40E0F7y!#kg*r6Gz#qIzTF^YV)!#{aQhX_ zf|q!iau1J8KsoEFcS+nDKR3hV{?g?7jb%~lsM))pyai!F+jrMLIR(VV4ANumwbC1D z)O9DaPQO82iq}l6E)3U9H4cv;XPhQ65f+rPv56RM>Xpp#Br}Kc@7~rfe=Zt)lRIat zW0Zf1a6i^wS#ADQ00Q!0IF>|u$X6LX(~o>I%2eBv3_m&U6cTf_wFnu27Q}jU&pRYl zs)@JiNkI@`ldYdk2+w%<<@d-c;u8Jbc}51%W%3(gt*84+n$s{q@dm1z@}L0h60-K0gXXgud}_=R2MgMZY=)k>G6d`83z1> zBrp4vkS0YC$Z1RS4jazn513_ZUB?Ycrm*TW>diwU^-FEGAcKc_6zLp9N~bx`Q?s+j zc2_33AG{dPZEa1LH)B)e07x?%-5c))G69~BJslz8eY91v?1z^Ih^HmVXE@j?F`@*v zF0d{xz+k*|S=IRky9bLHp)clz`jDF!IvrRu?;_~T_;?qkb*yWUuVgD#+K{WY&fIQk zzj6gZwx!2o0Rut~MsEp!`s2t($-I<2l6jMzB%+C)rv!bJrT1{i4qIMUV|m+$s5C{Nd#zAW*sXDB zm|NJea!J7k2bBRO)MA}d6B+Q`ar{#c(Crt~R}aBxdExpa+`ZsqiPw}8VG#Bi&MQS8 zlbW}ZH0A9SD}k?x`LIVTtQoiM-seLR)5h~oA!JP5N!C={0yVRWCw9`R6@MI4X}Pp z+A%P=bI`n}*qiFyB}srrj{+rHAEBPnDzZ$A214HIs=oeVS&ksclyKBg(WB>66Xa}w zbOp`k*AF;!bD6Qq)2_9nlV_9~1!`=3e6eY>`VNN&>WXA620!aV0?Rth=s^lrMQSeW%1zGPsy_;2)>WP&$MFvvA=z&R>P?Y z%y_hqarf!GlOo>TI*-S6F}`<$TDSmug`%1P9`R@KPm+}Z#&P+l?SK? zMpLKv`x5-7(DUyLL)E44VQ1Q?$#u1-4z?WsfhL!!xC zpd)L{3~ybapCmW-6eCDNPrn1!W}2;LnTG5iX|`>bIZxuDpm=1QvZ)XdY+O;?l+tiu zD$IfV*;B-#pCayk?t2A&oh7szwM0JMK-Hw~xMBpBbF-_4p7!5#KVSh(DyfTg%ATrb zl3aWrid<++DNG*KZ44_N3jfU(X}Y|9KAT~?Un*6Os-=H$^&yC2m4bYXUSAGXyRIsX z;cXLeWnZkYgnPK;Nh_!E2820st+>i8tTvx=F%?FxRZYPOM5ekpmfv z&Crrwl^%ay8w?!r*g!HoYxhf06i=3TN)wCPLANXYtQ&KLz%-3(vD4Yyi@FvP*{%k*y1QBn5%L!I2C3`mBq_gV{Z(=}raC3hw zI*0s|$|EHFASRW-nhdp(_|Gjns`N$)H+xrqiU^BR2rX)2#Z8DK#mB7#Q`MuG;g)2C zK^4}iDO+EP#OSOZ%$9!q)j!;`en^gw`&-nS?p}WK&-K%sI1@PtORbBbx!xYz5Ehtu z=J($wr?Kt(nU7UkDzXz$A+<4~a`u5t2hTn`4rmp;TgH>GSSt0kL)`z^NAF50)s!8V zKBJPaN~bKI{Iuyp%ubTdbp#l}zetU{k=78dEA++=*{Nhj3{y{-zH<*9C1-O4$w{Bkbp&`Y&{WZe##>vC+^8RIQ- z>3OWdLoG!*)0zUiu^~Htb-uZ7kGv%KL_4b28vofB+sZvs%wS_>iwpcyWYv8$sta|F zf%`56eNo7~O@96MZ3-cccff4RICAn+lmSVX$QiAimiiSyDgmG}CBC=X4jWJ25@kmi zPd^^#K-2!QvF?C@C-*U*^ISOa_Lu((5iG%Qh_2p{w#i& zJ?}Q%MB@H^Q`L-FB7wxx_nW?dD~U4nP8SlaA!XoCMyU=knk{>^`F7s@S^Ckj zz8oJw#G4gZ{aAFJZkS`l6kdG)?8=ZzV;$7DDg;(UV_#vOdfnRRa@X&io<`d?g%JK& zZ0wLM2z=k86<{e{Scjtyvsmc$UT_T!4aGfpmSxw0#cDxQXRV>D`4!dlMl`2}g^q3& z-qDFV%!{?vF*{1)Idj>~ZR2}g;2AnGd!rSma@o71=73x+;4yx~QIMwH&ryJl5Z-pf zDA;&<5giUoJ(42GXx+2VC=2V%22)&(APR-upCy;)`f^Y1|0zFPE#6s4zYy`k1QD(c z6YgKn_^hSHk*EylR^$byy{E<@TbV2P>o8;iH`hbc-RRN;4xt*>YN~dT z?lIP3W{FG_ck`i^MCv>K#NG(SsFvxoZZk7G!`dJwLuY zIn^O|QBfr18@K-w-iOC^7+X3c(mI$w#QQF|pxGCQ4T1Pl14^?#52H&m&1HYypY61v z&JSiwR(6S`v!tM)P^X%b={l)s&JPA0BETO!4+?ta(fweiZmQ7~FFlH?yo$YV_!YuA zrjP>500yl@_CzB_JMxP>rqf=P_rQcoBBC=sU+)hX`oF(>AD7p7%;{y59WmU@H|0L1 zEmMLrwurvh07*l``cNJ8fetn+t@cEga(cokrN((9;j{cGmI{e)gi=;+Nbqvh(7ZQE zd@THo9F&{Q-;>P_#jCNtJj0+m?UTf=_IDtuteo@SO4A{5+{GMrTFpP>>3w&;3q+;-WP_9LX<^OPo51o;fHE8ZfnhZcs_rYcJD%pXYL9!x#NxM9hcyZ*+|SP`CJ?a^&D&9^A4Ig&>iWt+z}HmvFjBU#xHONcOuo_J zK!OrSsb4;swJx0@y>`c`v1f7Yy$j;+*q2iqkgRgMUpXx17oQY{Lv2Sj+T6nE9CcsU zhGZq$GGwV%<13$Z2g9}7IG3AP65i+UjZ`p&4=Ba0G!d}cn0hBEwWk|ION=>xWO;4G z!%x&euZA`vx^0I(0y+_N z@Y&~T$U-~>ww~*O_~~-;C!CLet#P#4xK!0fbjNycG(nzEYd6d5@$S$(qsCR!2~Z74 zksuydlzF8CI$ol{biOt{`zl`dq9L(4(UsStheB1%>}A2AaN_wl?S0K}Td5u{zz&oS z&6(6eZa1}F8*@aO^M_lcZ0bRPvYR!a^XWntOiU}AV6)T^zC%W0>AJggRZHvcdyH2y zUG;5ehqThkA%Y?&m|~gat~_h(+z3DtI#2~%2`e6ZTn;4o_(gWI2>Hg3q=^RjLzt6% zcJpS>r+8k4pU9^jgEVn)8dfeGspIUDymy22rCde%f*}P;Pfps}Q-a@z*Bko6s6WJ! z5d5z4Ag}~eAy99f4NG*Z?S-gw&esrY>hFcq%Ds=D$WpA4+bYuMwO%a*Ca{cD*F36X z^9h#nLYb^Z(ezbRMSEQ<3E5vMh>SL+gcy{(J^taPUN&uOq}R9wLzzN-j&f_f+b6gI z?rc_W*+ZCNI=tfbqsv|>ml>83&ZFve$8LnpVv2=`6}Ucp$1}z+@=^W8%HC$!<-fUm z*xRNhaFMCW{AIJIwl+F8RmtP(GkxiEe}0vRu!WH>9{7*B+HV(0GP8d}r#HVl5lJ&7 zVq+)z2jbq|`y?7L-Ul*r9Clzlu#kKgvdg;l6rKR8w$pOG zkJB!>Dy5S#Jf4kjBEoWmEt_6%rLuDAKYmF-JU3gVWd3i2G0SUzdi*So#K9K-2pZDd*4kuxB6@0VLIhG~ zQ&jd@82!dG(+!V&Au>FQs1Ybi0l{Kf_AOe`2rt0AESp;jmOV?l(`;D?O)-yh>^h+< zzghg{*xxPBLO{X}x!k|g(xjSe{M^!|XVm&(9>Xj=W>Op0w9j$N_hI~Tgm4pHuUV#ZJaeuP1Uo2r z6Tk@UdPC!Xo}6uzfj4Z5dTSMpQ4Euq!PXsT38dZ5j`#JoH`dI4!0x7MAxT?^(~QKl z@cvoS#g9Key?Dol{;S8;sQBKq!;XOl^DVcRjeQ-vvqOe^jnE)1nld$|b}C8VQ_nWT z8;F4D?YHG9P!b%qYj??j9|8wlukqT);bo{ZZ?ICK1x>JVE*qH(%ijdZt@yP^CZ}^p zS2dE{b0o}m6Is^ddDo%g=!z7@0AGCZP!+i_d$_5>~7xwrAOxLKUCewmJ$tZLio9rm%P z`VV_0jD4&W={OibvSsqAp0$sd#*HYlu0iIbiJ^2_MOKr*>!)q^z=0=m*%F&ZBE6i- zw+n^O)O0)$%9mT;ZLT0(^OP7yP~QXAL-um9{x?coVE7Nj6bcP5QLkv10W=pGJ(P*# znvDNvT+BX!;KM(EP?|2||L|H*{}+iRzx@B1m3e@(IzpM4;@{27cK?Uy8a3_ufNH;$ uaRydQxD$X^QH@A7-&QUD`SV{-Zm^|7c^TuQYqGF_pTb+!H)YaB0sjvhc*NEK 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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 62a42723f995279fcaa4a63d9b0be061d32c66ca Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 10 Jan 2013 11:49:15 -0800 Subject: [PATCH 17/19] 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 18/19] 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 19/19] 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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